When grpc-web (browser) receives a 400 or 500 level error, metadata is empty
Author: EraYaNCreated Aug 5, 2026Updated Aug 5, 2026
It seems like the error object in the following snippet does not contain any metadata it also does not use the actual message from grpc-message header. (error as any).metadata returns an empty map.
let client=new GrpcWebClientBase(this.settings);
let client.rpcCall(
host + path,
req,
metadata?.toObject() ?? {},
descriptor,
(error, data) => {
if (error) {
obs.next(new GrpcStatusEvent(error.code, error.message, new GrpcMetadata((error as any).metadata)));
obs.complete();
} else {
obs.next(new GrpcDataEvent(data as any));
}
},
);resulting object in the obs Observable is { statusCode: 8, statusMessage: "Http response at 400 or 500 level, http status code: 429", metadata: {} } so the code does get parsed, but for some reason in the grpc-web code it does not recognize keep the metadata (or the original message)
Excerpt from grpcwebclientreadablestream.ts
// There's an XHR level error
let xhrStatusCode = -1;
if (lastErrorCode !== ErrorCode.NO_ERROR) {
switch (lastErrorCode) {
case ErrorCode.ABORT:
grpcStatusCode = StatusCode.ABORTED;
break;
case ErrorCode.TIMEOUT:
grpcStatusCode = StatusCode.DEADLINE_EXCEEDED;
break;
case ErrorCode.HTTP_ERROR:
xhrStatusCode = this.xhr!.getStatus();
grpcStatusCode = fromHttpStatus(xhrStatusCode);
break;
default:
grpcStatusCode = StatusCode.UNAVAILABLE;
}
if (grpcStatusCode === StatusCode.ABORTED && this.aborted) {
return;
}
let errorMessage = ErrorCode.getDebugMessage(lastErrorCode);
if (xhrStatusCode !== -1) {
errorMessage += `, http status code: ${xhrStatusCode}`;
}
this.handleError(new RpcError(grpcStatusCode, errorMessage));
return;
}We use the metadata for the extended/rich status header. Currently we are testing with 429 http responses.
Source: grpc/grpc-web