question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

metadata is lost when server sends StatusRuntimeException

See original GitHub issue

I 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:closed
  • Created 7 years ago
  • Comments:5 (4 by maintainers)

github_iconTop GitHub Comments

1reaction
skyaocommented, Aug 23, 2016

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:

  1. catch exceptions in onHalfClose()
  2. convert it to a customized exception with extended status code (0-16 is not enough for us) and metadata
  3. log the exception
  4. send the status and metadata to client by method ServerCall.close()
private static class ExceptionListener extends ServerCall.Listener {
    .....
    public void onHalfClose() {
            try {
                this.delegate.onHalfClose();
            } catch (Exception t) {
                ExtendedStatusRuntimeException exception = ExtendedStatusRuntimeException.fromThrowable(t);
                logger.error(....)
                call.close(exception.getStatus(), exception.getTrailers());
            }
        }
}

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?

0reactions
daicodencommented, Feb 16, 2017

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

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?

There is a plan to have a interceptor that can be installed that would convert an exception into a clean status (with trailers)

Sounds great, any movement on that?

Read more comments on GitHub >

github_iconTop Results From Across the Web

gRPC-Java: exception message - Google Groups
The problem is that unless we pass a StatusException or StatusRuntimeException, the message is lost. Unfortunately, we don't think it is safe to ......
Read more >
grpc/grpc - Gitter
Hello! I'm looking to know if there is the possibility to implement a single server streaming to multiple clients – implementing sort-of a...
Read more >
How to pass Java exceptions from arrow flight server to client ...
It throws the FlightRuntimeException exception with correct status code and description as I had set on the server side but ErrorFlightMetadata ...
Read more >
Exception Handling and Error Propagation in gRPC Java
We created a simple server and client to it, which sends the request and outputs the response to the console. Note: on the...
Read more >
GRPC Core: Status codes and their use in gRPC
Code Number Description OK 0 Not an error; returned on success. FAILED_PRECONDITION 9 OUT_OF_RANGE 11
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found