metadata is lost when server sends StatusRuntimeException
See original GitHub issueI would like to send a StatusRuntimeException to the client with some information saved in metadata. like this:
Metadata trailers = new Metadata();
trailers.put(EXTENDED_STATUS_KEY, "10001");
StatusRuntimeException e = new StatusRuntimeException(Status.ABORTED, trailers);
throw e;
But when I caught the exception in the client, I found that the metadata in the exception didn’t contain this information. I checked the code and found that in class Server.JumpToApplicationThreadServerStreamListener:
public void halfClosed() {
callExecutor.execute(new ContextRunnable(context) {
@Override
public void runInContext() {
try {
getListener().halfClosed();
} catch (RuntimeException e) {
internalClose(Status.fromThrowable(e), new Metadata());
throw e;
} catch (Throwable t) {
internalClose(Status.fromThrowable(t), new Metadata());
throw new RuntimeException(t);
}
}
});
}
In this method, Status.fromThrowable(e)
will convert this exception into Status instance, but it won’t reuse the metadata saved in the exception. Instead, it create a new empty Metadata.
So in this way, all the data saved in the metadata of StatusRuntimeException is lost.
Yet in class Status I found there is a new method Metadata trailersFromThrowable(Throwable t)
and it is used in class ServerCalls:
public void onError(Throwable t) {
Metadata metadata = Status.trailersFromThrowable(t);
if (metadata == null) {
metadata = new Metadata();
}
call.close(Status.fromThrowable(t), metadata);
}
I think this is the correct way to handle and pass metadata.
Please help to confirm it. If so, I would like to pull a request to fix it.
Thank you !
Issue Analytics
- State:
- Created 7 years ago
- Comments:5 (4 by maintainers)
Top GitHub Comments
OK, I understand.
But sometimes, it is more comfortable for the developers to throw exception during business handling where they want to say “there is something wrong” to the client.
I have tried to add a ServerCall.Listener to do this after you reject my PR:
For this case: "Most notably, if a server issues a call to another backend and that call fails (with a StatusRuntimeException), the error may contain information that should not be disclosed with the server’s clients. ".
We will catch all the exceptions when we call a grpc server and map them to our business logic, we won’t let the StatusRuntimeException go through our server and pass to our client. So in this case, we would like to use our listener or the one you planed.
Thank you so much for your help. In fact, I really don’t know that we should use onError() to handle exception at first. This is an important design principle, but I don’t get this information before, is there any documentation about it? Or we need to document it?
I’m trying to get error propagation across three services (a -> b -> c). Clients talk to a. The goal is if an error is thrown in C, you can see a chained stack trace in A so you can identify it’s C quickly. Is there a way to do this currently without passing the responseObserver around?
Sounds great, any movement on that?