Actionable diagnostic for a header-identified saga over gRPC (GH-3385)#3403
Merged
Conversation
A header-identified saga cannot work over a gRPC hop: the saga-id envelope header is not propagated across the call, so no id can be resolved. That is unchanged and is not what this fixes. What it fixes is the diagnostic. The caller used to get StatusCode.Internal with "Could not determine a valid saga state id for Envelope ..." -- true, but it names no cause and no remedy, and the remedy is not guessable, because the reason the id is missing is a property of the transport rather than of the message. It now maps to StatusCode.InvalidArgument with a detail that says the identity must be on the message body, why (the header does not cross the hop), how to supply it ([SagaIdentity] or a matching property), and where to read more. InvalidArgument rather than Internal is deliberate per AIP-193: the request cannot succeed as sent and retrying will not change that -- it is a caller-side contract problem, not a transient server fault. The mapping lives in WolverineGrpcExceptionMapper, which only runs at the gRPC boundary, so the core exception is untouched and non-gRPC users see no change. Message-identified sagas over gRPC already work and are unaffected (covered by #3386). Per @erikshafer's analysis on the issue, full three-point saga-id propagation is deliberately NOT taken: HTTP does not exercise the header path either, so "works like HTTP" is already satisfied, and the complexity is not warranted until someone shows a concrete need. Flips the #3386 characterization test, and updates the gRPC + Sagas docs page. Full Wolverine.Grpc.Tests 273/273. Full wolverine.slnx Release build clean. Closes #3385. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This was referenced Jul 14, 2026
This was referenced Jul 18, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3385. For 6.18.0. Follows @erikshafer's analysis on the issue — thank you for the legwork, including the #3386 coverage this builds on.
What is NOT changing
A header-identified saga still cannot work over a gRPC hop. The
saga-idenvelope header is not propagated across the call, so no id can be resolved.Per @erikshafer's framing, full three-point
saga-idpropagation is deliberately not taken: message-identified sagas already work over gRPC (proven by the merged #3386, documented in #3389), and the header path is a transport concern that HTTP does not exercise either — so "works like HTTP" is already satisfied. That stays a future option, not a debt.What IS changing: the diagnostic
Before:
True, but it names no cause and no remedy — and the remedy is not guessable, because the reason the id is missing is a property of the transport, not of the message in front of you.
After:
InvalidArgumentrather thanInternalis deliberate (AIP-193): the request cannot succeed as sent and retrying will not change that. It is a caller-side contract problem, not a transient server fault — andInternalactively invites a client to retry something that can never work.Where it lives
WolverineGrpcExceptionMapper, which only runs at the gRPC adapter boundary. The coreIndeterminateSagaStateIdExceptionis untouched, so non-gRPC users see no change and no gRPC-specific wording leaks into the framework's general saga errors.Verification
..._fails_with_opaque_status_today→..._fails_with_an_actionable_diagnostic), now pinning the status code, the cause, the remedy, and the docs link.Wolverine.Grpc.Tests273/273 — the mapper is shared, so the whole suite matters here, not just the saga tests.wolverine.slnxRelease build clean.🤖 Generated with Claude Code