[AAASM-4474] 🐛 (langgraph): Add missing idempotency guard for sync subgraph invoke - #237
Merged
Chisanan232 merged 1 commit intoJul 11, 2026
Conversation
AAASM-4472 added _wrap_subgraph_invoke_methods_in_place() to inject lineage tracking into a compiled subgraph's own invoke/ainvoke when it is reused as a node. The async path guards against double-wrapping via an _agent_assembly_ainvoke_spawned marker, but the sync path had no equivalent guard: reusing the same compiled subgraph as a node in two different parent graphs re-wrapped .invoke on the second compile, stacking spawn-context depth (2 instead of 1) on a single sync invoke. Add a matching _agent_assembly_invoke_spawned marker checked before wrapping .invoke, mirroring the existing async guard exactly. New regression tests: a sync test reproducing the double-wrap (mount the same compiled subgraph in two parent graphs, invoke synchronously, assert lineage depth stays 1) and a symmetric async test confirming the already-correct async path stays correct. Closes AAASM-4474
|
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Chisanan232
deleted the
v0.1.0/AAASM-4474/fix/langgraph_sync_invoke_idempotency
branch
July 11, 2026 15:05
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.



What changed
_wrap_subgraph_invoke_methods_in_place()inagent_assembly/adapters/langgraph/patch.py(added by AAASM-4472) now guards the sync.invokewrap path with an_agent_assembly_invoke_spawnedmarker, mirroring the existing_agent_assembly_ainvoke_spawnedguard already used for the async.ainvokepath.Why
AAASM-4472 fixed lineage tracking for a compiled subgraph reused as a node (
parent_graph.add_node("sub", compiled_subgraph)) by mutating the subgraph's owninvoke/ainvokein place. The async path correctly no-ops on a second wrap via its marker check. The sync path had no equivalent guard.Root cause: if the same compiled subgraph instance is mounted as a node in two (or more) different parent graphs — a legitimate reusable-subgraph pattern — each
compile()call re-wraps.invokearound whatever.invokecurrently is. On the second compile,original_invokecaptured inside_wrap_subgraph_invoke_methods_in_placeis already the first wrapper (not the true original), so invoking the first parent graph synchronously nests two spawn-context scopes: the outer sets depth1, the inner then computes depth_current_spawn_ctx.depth + 1 = 2. The subgraph's own node observes lineage depth2instead of1.Fix: add
_agent_assembly_invoke_spawned, set on first wrap and checked before wrapping.invokeagain — same naming convention and same check logic as the existing async guard, so the two guards read as a matched pair.Test evidence
Two new tests added to
test/integration/test_langgraph_real_package_smoke.py, alongside the existing (unchanged) AAASM-4472 testtest_real_stategraph_subgraph_as_node_propagates_spawn_lineage:test_real_stategraph_reused_subgraph_across_parents_sync_invoke_lineage_depth_stable— mounts the same compiled subgraph as a node in two different parent graphs, invokes the first synchronously, asserts lineage depth is1.test_real_stategraph_reused_subgraph_across_parents_async_ainvoke_lineage_depth_stable— symmetric async version (.ainvoke), asserting the already-correct async path stays correct.Before fix (TDD — wrote the sync test first against the unpatched code):
After fix:
Full check suite
pytest test/— 782 passed, 16 skipped (pre-existing skips: native_coremodule not built, optional frameworks not installed — unrelated to this change), 0 failed.ruff check .— all checks passed.ruff format --check .— no formatting issues in the changed files (6 unrelated pre-existing files elsewhere in the repo would reformat; not touched here).mypy agent_assembly— same 4 pre-existing errors present onmasterbaseline (native_corestub / grpc stubs, unrelated topatch.py); no new errors.pre-commit run(scoped to changed files) — all hooks passed (isort, autoflake, black, mypy, etc).How to verify
Closes AAASM-4474