Skip to content

Actionable diagnostic for a header-identified saga over gRPC (GH-3385)#3403

Merged
jeremydmiller merged 1 commit into
mainfrom
grpc-saga-diagnostic-3385
Jul 13, 2026
Merged

Actionable diagnostic for a header-identified saga over gRPC (GH-3385)#3403
jeremydmiller merged 1 commit into
mainfrom
grpc-saga-diagnostic-3385

Conversation

@jeremydmiller

Copy link
Copy Markdown
Member

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-id envelope header is not propagated across the call, so no id can be resolved.

Per @erikshafer's framing, full three-point saga-id propagation 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:

StatusCode.Internal — "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, not of the message in front of you.

After:

StatusCode.InvalidArgument — "Could not determine a saga id for this request. A saga started or continued over a gRPC hop must carry its identity ON THE MESSAGE BODY: the 'saga-id' envelope header is not propagated across a gRPC call, so a header-identified saga cannot work over gRPC. Put the saga identity on the request message itself (a property Wolverine can match to the saga id, or one marked with [SagaIdentity]). See https://wolverinefx.net/guide/grpc/sagas.html"

InvalidArgument rather than Internal is 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 — and Internal actively invites a client to retry something that can never work.

Where it lives

WolverineGrpcExceptionMapper, which only runs at the gRPC adapter boundary. The core IndeterminateSagaStateIdException is untouched, so non-gRPC users see no change and no gRPC-specific wording leaks into the framework's general saga errors.

Verification

🤖 Generated with Claude Code

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>
@jeremydmiller
jeremydmiller merged commit 4932cfd into main Jul 13, 2026
25 checks passed
This was referenced Jul 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

gRPC service path doesn't propagate saga-id — header-identified sagas can't be started/continued over gRPC

1 participant