A gRPC hop that keeps the error. A service throws a classified error; its caller catches the same error, with the same kind, code and payload, as though the call had been local.
Everything is opt-in and separately installable — errors, discovery, credentials — so a client takes what it needs and nothing else. Only the server package requires ASP.NET Core.
dotnet add package ApricotFramework.Grpc.Server # the server
dotnet add package ApricotFramework.Grpc.Client # the caller
dotnet add package ApricotFramework.Grpc.Client.DiscoveryClient # ...addressed by service name
dotnet add package ApricotFramework.Grpc.Client.Authentication # ...presenting its own token// Server. AddGrpc stays gRPC's own; this says what happens when a call fails, asking the same
// exception mappers that already answer this service's HTTP requests.
builder.Services.AddGrpc();
builder.Services.AddGrpcErrorHandling();// Anywhere in a service method. Nothing catches this; the caller receives it.
throw Err.NotFound("ORDER_NOT_FOUND", $"no order with id '{id}'",
new Dictionary<string, object?> { ["orderId"] = id }).AsException();// Caller. Each line is a capability, and any of them can be left out.
builder.Services.AddGrpcClientsCore(builder.Configuration);
builder.Services.AddGrpcErrorMapping();
builder.Services
.AddDiscoveredGrpcClient<Orders.OrdersClient>("orders")
.AddGrpcCallCredentials(credentials => credentials.Scopes = ["orders.read"]);try
{
var order = await client.GetOrderAsync(new GetOrderRequest { Id = id });
}
catch (ErrorDefinitionException failure) when (failure.HasKind(ErrorKinds.NotFound))
{
// The kind, the code and the payload are the ones the orders service threw.
}Note. Errors travel in a trailer, and a trailer too large for the peer fails the whole response. They are therefore trimmed to a byte budget — payloads first, then the errors after the first — with a flag on the wire saying so. The server's log always holds the full set.
The wire contract is apricot.errors.v1, packed into the contract package as
protos/apricot/errors/v1/errors.proto so a service in another language can speak it.
Full documentation at projectapricot.dev/docs/grpc.