Accept *Async suffix on saga method names (#2578)#2593
Merged
jeremydmiller merged 1 commit intomainfrom Apr 26, 2026
Merged
Conversation
HandlerDiscovery has always treated saga methods named StartAsync / HandleAsync / OrchestrateAsync / ConsumeAsync / NotFoundAsync as valid — it strips the Async suffix when matching against the canonical method name list. But SagaChain.findByNames matched on strict string equality, so async-suffixed methods were discovered into the handler graph and then silently dropped from StartingCalls / ExistingCalls / NotFoundCalls. The generated chain would construct the saga but never invoke the user's method, leaving Saga.Id == Guid.Empty and throwing on insert with a confusing "must define the saga id" message. Fix: SagaChain.findByNames now accepts both the bare name (e.g. "Start") and its async-suffixed twin (e.g. "StartAsync"), making it symmetric with HandlerDiscovery. Tests: Bug_2578_saga_async_method_names exercises both halves — discovery-level (StartingCalls / ExistingCalls / NotFoundCalls populated for *Async methods across Start, Handle, Orchestrate, Consume, StartOrHandle, NotFound) and end-to-end (StartAsync / HandleAsync are actually invoked, saga state mutates and persists). 8/8 fail on main, 8/8 pass with this change. All other 1352 CoreTests still pass. Docs: docs/guide/durability/sagas.md "Method Conventions" section now documents that every name accepts the *Async variant for Task-returning methods. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This was referenced Apr 28, 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.
Fixes #2578.
The bug
HandlerDiscoveryalready treats saga methods namedStartAsync/HandleAsync/OrchestrateAsync/ConsumeAsync/StartOrHandleAsync/NotFoundAsyncas valid — it strips theAsyncsuffix when matching against the canonical name list (HandlerDiscovery.cs:54-55). ButSagaChain.findByNamesmatched on strict string equality, so async-suffixed methods were:StartingCalls/ExistingCalls/NotFoundCalls,Saga.Id == Guid.Empty,ArgumentOutOfRangeException: You must define the saga id when using the lightweight saga storage.The reporter's observation: the failure mode is uniquely confusing because the documentation already mentions
StartAsyncas if it works (docs/guide/durability/sagas.md:374).The fix
SagaChain.findByNamesnow matches both the bare name and its async-suffixed twin:This is option 1 from the issue's proposed fixes — making
SagaChainsymmetric withHandlerDiscovery's convention.Tests
Bug_2578_saga_async_method_namescovers both halves:Discovery-level —
StartingCalls/ExistingCalls/NotFoundCallsare populated for*Asyncmethods across every saga convention (StartAsync,HandleAsync,OrchestrateAsync,ConsumeAsync,StartOrHandleAsync,NotFoundAsync).End-to-end —
StartAsyncandHandleAsyncare actually invoked by the generated handler, saga state mutates correctly, and persists inInMemorySagaPersistor.8 new tests. All 8 fail on
main, all 8 pass with this change. All 1352 other CoreTests still pass (1360 total).Docs
docs/guide/durability/sagas.md"Method Conventions" section now documents the*Asyncvariants alongside the existing convention table, with a small code sample.Test plan
Bug_2578_*tests fail without the fixBug_2578_*tests pass with the fix🤖 Generated with Claude Code