grpc / grpc-web

gRPC for Web Clients
https://grpc.io
Apache License 2.0
8.65k stars 764 forks source link

TypeError: b is not a function. (In 'b(null, d)', 'b' is undefined) #1044

Open christopher-wong opened 3 years ago

christopher-wong commented 3 years ago

Hi there,

I'm having some issues invoking gRPC-web calls from my React application. Invoking my RPC works correctly and the server receives the request without issue, however an error appears in my console on every request.

index.js:60 Uncaught TypeError: b is not a function
    at Array.<anonymous> (index.js:60)
    at yc (index.js:53)
    at S.<anonymous> (index.js:51)
    at Ib (index.js:34)
    at O (index.js:33)
    at oc (index.js:42)
    at S.push../node_modules/grpc-web/index.js.S.O (index.js:40)
    at S.push../node_modules/grpc-web/index.js.S.K (index.js:40)

I'm running a gRPC server in Go with a React application communicating with the Go server via the Envoy proxy.

I compile my Proto using the following command:

protoc \
        --js_out=import_style=commonjs,binary:web-client/src/ \
        --grpc-web_out=import_style=commonjs,mode=grpcwebtext:web-client/src/ \
        proto/myapp.proto
$ protoc --version
libprotoc 3.14.0

and I build my request from React like so:

const { GetMessagesRequest, Message } = require('../../proto/myapp_pb.js');
const { MyappClient } = require('../../proto/myapp_grpc_web_pb');

const myappService = new MyappClient('http://localhost:8080');
const msg = new Message()

const meta = {
    "authorization": "fake_123456",
  }

await myappService.broadcast(msg, meta)
alertedsnake commented 3 years ago

Similar issue, protoc 3.13.0, same compile command.

index.js:60 Uncaught TypeError: b is not a function
    at Array.<anonymous> (index.js:60)
    at X (index.js:53)
    at S.<anonymous> (index.js:51)
    at Ib (index.js:34)
    at O (index.js:33)
    at oc (index.js:42)
    at S.push../node_modules/grpc-web/index.js.S.O (index.js:40)
    at S.push../node_modules/grpc-web/index.js.S.K (index.js:40)
alertedsnake commented 3 years ago

I can confirm that with 1.1.0 this problem does not exist, so it was introduced in the 1.2.0 release.

alertedsnake commented 3 years ago

My coworker found the issue here - before, my call looked like this:

function doCall() {
  return new Promise((resolve, reject) => {
        client.rpcCall(request)
            .on("data", () => resolve())
            .on("status", () => {})
            .on("error", response => reject(response.message));
   });
}

But now it needs to look like:

function doCall() {
  return new Promise((resolve, reject) => {
        client.rpcCall(request, null, (err, response) => {
            if (err) {
                reject(err.message);
            }
            else {
                resolve();
            }
        });
   });
}

Hopefully this may help you as well.

jackzzj commented 2 years ago

I'm getting the same error with 1.3.0

sampajano commented 2 years ago

Hi! Thanks for the bug report and sorry for the delay!

I'm curious if this bug is specific to the Go grpc server, or this is more common than that?

I'm not quite able to reproduce this issue on the nodejs + commonjs example that we have here. So i will need some help to reproduce.. :)

If someone could create a minimal repro in Docker maybe i could try it out and debug it.

Thanks! :)

Enrikerf commented 2 years ago

Thanks a lot for that piece of code @alertedsnake there are some similar syntax for a server stream?

I want to isolate the protobuf code from my component so I want to process each batch of the stream separately


import {ResultServiceClient} from "../protobuf/generated/result_grpc_web_pb";

const resultMessages = require('../protobuf/generated/result_pb')

export class ResultService {
    client: ResultServiceClient
    messages: any
    stream: any

    constructor() {
        this.client = new ResultServiceClient("http://localhost:8080", null, null)
        this.messages = resultMessages
    }

    getStream(batchUuid: string) {
        let streamResultsRequest = new resultMessages.StreamResultsRequest()
        streamResultsRequest.setBatchUuid(batchUuid)
        this.stream = this.client.streamResults(streamResultsRequest, {})
    }

    // something like this but get each batch as a promise not only the first one or all together
    getData() {
        return new Promise((resolve, reject) => {
            this.stream.on('data', function (response: { getResultsList: () => any; }) {
                resolve(response.getResultsList())
            });
        })
    }
}
alertedsnake commented 2 years ago

@Enrikerf oh - I'm FAR from the right person to be asking, I do very little Javascript, not sure I can really help much :)