grpc-dotnet icon indicating copy to clipboard operation
grpc-dotnet copied to clipboard

Is it possible to intercept/handle errors thrown by Grpc.AspNetCore.Server.ServerCallHandler?

Open erikljung opened this issue 5 years ago • 6 comments

Hi, I'm working on error handling for a project and I'm trying to understand if it's possible to intercept or handle errors that seems to be thrown very early in the pipeline. I've tried interceptors, but seems like we never get there. Is there any suggested way to handle this kind of errors?

2020-10-14 14:18:08 [ERR] Grpc.AspNetCore.Server.ServerCallHandler
   {"EventId":{"Id":14,"Name":"ErrorReadingMessage"},"RequestId":"0HM3G7CQSMESD:00000001","RequestPath":"/MyProject.SomeService/SetDisplayName","SpanId":"|bde9d0fa-4c539042cc801db0.","TraceId":"bde9d0fa-4c539042cc801db0","ParentId":""}
   Error reading message.
   System.FormatException: Unexpected Guid length: 37
   at ProtoBuf.Internal.ThrowHelper.Format(String message) in /_/src/protobuf-net.Core/Internal/ThrowHelper.cs:line 34
   at ProtoBuf.Internal.GuidHelper.Read(State& state) in /_/src/protobuf-net.Core/Internal/GuidHelper.cs:line 74
   at proto_8(State& , SetDisplayNameRequest )
   at ProtoBuf.Internal.Serializers.SimpleCompiledSerializer`1.ProtoBuf.Serializers.ISerializer<T>.Read(State& state, T value)
   at ProtoBuf.ProtoReader.State.ReadAsRoot[T](T value, ISerializer`1 serializer)
   at ProtoBuf.ProtoReader.State.DeserializeRoot[T](T value, ISerializer`1 serializer)
   at ProtoBuf.Meta.TypeModel.Deserialize[T](ReadOnlySequence`1 source, T value, Object userState)
   at ProtoBuf.Grpc.Configuration.ProtoBufMarshallerFactory.ContextualDeserialize[T](DeserializationContext context)
   at Grpc.AspNetCore.Server.Internal.PipeExtensions.ReadSingleMessageAsync[T](PipeReader input, HttpContextServerCallContext serverCallContext, Func`2 deserializer)

2020-10-14 14:18:08 [ERR] Grpc.AspNetCore.Server.ServerCallHandler
   {"EventId":{"Id":6,"Name":"ErrorExecutingServiceMethod"},"RequestId":"0HM3G7CQSMESD:00000001","RequestPath":"/MyProject.SomeService/SetDisplayName","SpanId":"|bde9d0fa-4c539042cc801db0.","TraceId":"bde9d0fa-4c539042cc801db0","ParentId":""}
   Error when executing service method 'SetDisplayName'.
   System.FormatException: Unexpected Guid length: 37
   at ProtoBuf.Internal.ThrowHelper.Format(String message) in /_/src/protobuf-net.Core/Internal/ThrowHelper.cs:line 34
   at ProtoBuf.Internal.GuidHelper.Read(State& state) in /_/src/protobuf-net.Core/Internal/GuidHelper.cs:line 74
   at proto_8(State& , SetDisplayNameRequest )
   at ProtoBuf.Internal.Serializers.SimpleCompiledSerializer`1.ProtoBuf.Serializers.ISerializer<T>.Read(State& state, T value)
   at ProtoBuf.ProtoReader.State.ReadAsRoot[T](T value, ISerializer`1 serializer)
   at ProtoBuf.ProtoReader.State.DeserializeRoot[T](T value, ISerializer`1 serializer)
   at ProtoBuf.Meta.TypeModel.Deserialize[T](ReadOnlySequence`1 source, T value, Object userState)
   at ProtoBuf.Grpc.Configuration.ProtoBufMarshallerFactory.ContextualDeserialize[T](DeserializationContext context)
   at Grpc.AspNetCore.Server.Internal.PipeExtensions.ReadSingleMessageAsync[T](PipeReader input, HttpContextServerCallContext serverCallContext, Func`2 deserializer)
   at Grpc.AspNetCore.Server.Internal.CallHandlers.UnaryServerCallHandler`3.HandleCallAsyncCore(HttpContext httpContext, HttpContextServerCallContext serverCallContext)
   at Grpc.AspNetCore.Server.Internal.CallHandlers.ServerCallHandlerBase`3.<HandleCallAsync>g__AwaitHandleCall|8_0(HttpContextServerCallContext serverCallContext, Method`2 method, Task handleCall)

Or am I stuck with these errors and have to rely on Grpc.AspNetCore.Server.GrpcServiceOptions.EnableDetailedErrors to get a more detailed error? The issue with this method might be that I don't know which details that are leaked to the consumer, also the following error gives the status code Unknown, when it maybe could be a an InvalidArgument instead?

Thanks for your help. /Erik

erikljung avatar Oct 14 '20 12:10 erikljung

What are you trying to handle exactly? What's the end goal?

davidfowl avatar Oct 14 '20 12:10 davidfowl

In my example, protobuf-net.Grpc encounters a problem and throws an exception. I cannot find a way to catch this exception before it is turned into an error response and sent to the caller. One reason for doing this would be to attach a Correlation/TraceId to the response in order to help the caller with troubleshooting.

erikljung avatar Oct 14 '20 15:10 erikljung

Interceptors are called after deserialization.

ASP.NET Core middleware can be added to the app to catch and handle the error.

JamesNK avatar Oct 14 '20 20:10 JamesNK

Ok, that makes sense.

I feel confident that I've tried to handle this with regular middlewares, without success. Maybe I'm doing it wrong, do you have any sample/pseduo code?

erikljung avatar Oct 14 '20 21:10 erikljung

Hmm, I think you're right that an exception doesn't get thrown. The gRPC server library catches exceptions and then sets a grpc-status trailer.

You could look at the response for a grpc-status trailer that is not zero in your middleware, but you won't get the originally thrown exception.

This might be a reason to add a gRPC global exception handler configuration option.

JamesNK avatar Oct 14 '20 23:10 JamesNK

Yes, as you said the status code is in the trailer, but that seems to be it. Without the original exception there won't be much use unfortunately. And if there was an exception, would I be able to write a new response to the caller? The global exception handler sounds like the thing I'm asking for.

erikljung avatar Oct 15 '20 06:10 erikljung