grpc-java: Uncaught exceptions in server

Please answer these questions before submitting your issue.

What version of gRPC are you using?

1.23.0

What did you expect to see?

Error response after uncaught exception

Refer to the following file/line number: https://github.com/grpc/grpc-java/blob/ba0fd84d79d66f46b4492043c62ecb0fcf85ead2/core/src/main/java/io/grpc/internal/ServerImpl.java#L558

You can see that Exception types that are not subclasses of Error or RuntimeException (known as “checked exceptions”) are not caught here.

Uncaught exceptions result in no response being sent to the client (but the connection remains open) and thus, from the clients perspective, the RPC call hangs indefinitely.

All exceptions should be caught here, not just runtime exceptions and errors.

“Checked” exceptions can be thrown from code that does not declare it in the signature by several means. One example is here: https://stackoverflow.com/questions/15496/hidden-features-of-java/2131355#2131355

Another example is from any kotlin code that calls a java function that throws a checked exception.

NOTE: it looks like the same problem exists in multiple places in this file. The line number I linked is the specific one that I hit in the wild.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 1
  • Comments: 22 (12 by maintainers)

Commits related to this issue

Most upvoted comments

fwiw, here’s an example of “sneaky throws” in the wild that I discovered today while doing something completely unrelated. I believe it’s more evidence that GRPC should not be opinionated on this matter and should simply handle all Throwables, as the fact of the matter is that this is a feature of the JVM that people use, wisely or unwisely, Kotlin or no Kotlin:

https://github.com/testcontainers/testcontainers-java/blob/master/core/src/main/java/org/testcontainers/containers/PortForwardingContainer.java https://projectlombok.org/features/SneakyThrows

@ejona86 here’s the new workaround, which has similar behaviour and is hopefully now safe:

class GlobalGrpcExceptionHandler : ServerInterceptor {
    override fun <ReqT : Any?, RespT : Any?> interceptCall(call: ServerCall<ReqT, RespT>?, headers: Metadata?, next: ServerCallHandler<ReqT, RespT>?): ServerCall.Listener<ReqT> {
        val delegate = next?.startCall(call, headers)
        return object : ForwardingServerCallListener.SimpleForwardingServerCallListener<ReqT>(delegate) {
            override fun onHalfClose() {
                try {
                    super.onHalfClose()
                } catch (e: Exception) {
                    throw RuntimeException(e)
                }

            }
        }
    }
}