Endpoint 'gRPC - Unimplemented service'
See original GitHub issueI have a service that is not behaving as it should. While troubleshooting it, I noticed this debug output:
Endpoint ‘gRPC - Unimplemented service’ with route pattern ‘{unimplementedService}/{unimplementedMethod}’ is valid for the request path … (the path of the non-working service).
What does this mean? I register my Grpc-service using endpoints.MapGrpcService<InforARIService>();
The actual issue I was troubleshooting is that the Grpc-service is invoked, does it’s work correctly and returns a class with two string properties. However, on the wire the response is empty (so the client gets a serialization error):
HTTP/1.1 200 OK
Content-Length: 0
Content-Type: application/grpc-web
Date: Fri, 08 Apr 2022 09:23:39 GMT
Server: Kestrel
Request-Context: appId=cid-v1:87cb2164-3464-497d-a7b4-80e9471bf6c3
Grpc-Status: 0
X-Content-Type-Options: nosniff
Strict-Transport-Security: max-age=31536000
Issue Analytics
- State:
- Created a year ago
- Comments:7 (1 by maintainers)
Top Results From Across the Web
grpc server shows "unimplemented service error"
My grpc service interface definition and service implementation were declared in different namespaces but this does not cause a failure for code ...
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 >C# – grpc server shows “unimplemented service error”
I have created a grpc server and client after this instruction: https://docs.microsoft.com/en-us/aspnet/core/tutorials/grpc/grpc-start?view=aspnetcore-3.0&tabs= ...
Read more >Create gRPC services and methods
In this article. Create new gRPC services; Implement gRPC methods; Access gRPC request headers; Multi-threading with gRPC streaming methods ...
Read more >Grpc.Core.RpcException method is unimplemented with ...
I am having trouble finding the source of this error. I implemented a simple service using protobuf: syntax = "proto3"; package tourism; ...
Read more >
Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free
Top Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Thanks for the simple repro app. You can fix your issue by passing through response flushes immediately. This way the gRPC trailer-writing logic knows when the response has already started and does not incorrectly consider the response “Trailers-Only” adds the
grpc-status: 0
trailer as a normal header sent before the response body instead of after the response body:https://github.com/grpc/grpc-dotnet/blob/197b43bbf784603d3f2e286f33e43f8b1dbdef72/src/Grpc.AspNetCore.Server/Internal/GrpcProtocolHelpers.cs#L156-L175
The workaround:
And then the following change to
RequestGrabber.cs
Even though
ResponseBufferingStream
doesn’t actually flush any bytes to the response body that may have already been written to theMemoryStream
, flushing theHttpResponse.Body
always flushes the response headers which givesHttpResponse.HasStarted
its proper value oftrue
which is what’s important here.I did not override all the methods an app could use to try to flush the response, but I know the gRPC logic calls
HttpResponse.StartAsync()
which calls through to this when you replace theHttpResponse.Body
stream.I think this should probably be fixed in gRPC. If the gRPC logic independently tracked if it has written to the body instead of using
response.HasStarted
, the simplerMemoryStream
response body replacement could still work like it does for most other kinds of requests.At first, I thought this was going to a case of not flushing
HttpResponse.BodyWriter
before copying the response body. I was going to suggest changing the following change toRequestGrabber
:When you replace the response body with a custom Stream, this is equivalent to the following:
It still might be a good idea. Without doing this, the
await replacementResponseBody.CopyToAsync(originalResponseBody)
will not copy bytes that have been appended to theBodyWriter
viaGetMemory()/Advance()
without being flushed. AFAIK, gRPC doesn’t flush theBodyWriter
for performance reasons when doing normal HTTP/2-based gRPC. It looks like the response does get explicitly flushed whengrpc-status: 0
trailer is flushed for grpc-web responses.I think gRPC should be calling
response.CompleteAsync()
orresponse.BodyWriter.CompleteAsync()
before exiting its middleware even in the normal non-grpc-web case. This would get the same performance benefit has not flushing for non-wrapped response Streams, but it will flush the stream (without actually completing the real response) when the response Stream is wrapped. @JamesNKAlso, setting
body.Position = 0;
afterReadToEndAsync()
is unnecessary and probably wrong, but it’s not very likely to matter so early in the middleware pipeline.Possibly related: https://github.com/dotnet/aspnetcore/issues/39317
The above is why this issue is currenlty left open in the backlog in case that’s not clear.