Skip to content

fix(graph): preserve user-registered input KeyStrategy across state cloning and snapshots - #115

Merged
yuluo-yx merged 1 commit into
agentic-ai-java:mainfrom
konlue:fix/clone-state-keystrategy-aliasing
Oct 7, 2026
Merged

yuluo-yx merged 1 commit into
agentic-ai-java:mainfrom
konlue:fix/clone-state-keystrategy-aliasing

Conversation

@konlue

@konlue konlue commented Oct 7, 2026

Copy link
Copy Markdown
Contributor

Problem

Same as reported upstream in alibaba/spring-ai-alibaba#4999 (the maintainer
suggested submitting related fixes here):

  • CompiledGraph.cloneState passes the compiled graph's shared keyStrategyMap
    into the 2-arg OverAllState constructor by reference; the constructor
    unconditionally calls registerKeyAndStrategy(DEFAULT_INPUT_KEY, new ReplaceStrategy())
    (a put), so the first checkpoint (GraphRunnerContext.addCheckpoint) silently
    overwrites the user-registered "input" strategy on the shared map, affecting all
    subsequent runs and concurrent invoke reads.
  • Even with a defensive copy, the unconditional default-input registration still
    replaces user-registered strategies on every clone and snapshot:
    OverAllState.snapShot() (the defensive snapshot handed to each parallel branch in
    ParallelNode / ConditionalParallelNode) and the clone state passed to
    InterruptableAction hooks in NodeExecutor both lose e.g. AppendStrategy for
    "input".

Fix

In both the 2-arg and 3-arg constructors:

  • defensively copy the caller's strategy map (no aliasing with the caller-owned map);
  • register the default "input" strategy with putIfAbsent instead of put, so it
    only applies when the caller did not register one.

Behavior is unchanged on every path where the caller did not register "input";
the only behavior change is the buggy case above.

Testing

New CloneStateKeyStrategyPollutionTest (2 tests) fails on main with
expected AppendStrategy but was ReplaceStrategy (both the shared-map pollution and
the clone/snapshot overwrite) and passes after the fix.

  • mvn -pl argi-graph-core test -DskipTests=false: all green except 3 pre-existing
    FileSystemStoreTest errors caused by WindowsPath.relativize: 'other' has different root (test temp dirs across Windows drive roots, unrelated to this change).
  • mvn -pl argi-graph-core checkstyle:check spotless:check: 0 violations
    (per CONTRIBUTING, CI does not run on PRs, so these were run locally).

…loning and snapshots

The 2-arg/3-arg OverAllState constructors unconditionally re-registered DEFAULT_INPUT_KEY -> ReplaceStrategy and, when handed the CompiledGraph's shared keyStrategyMap (CompiledGraph.cloneState), mutated that shared map on every checkpoint. Defensively copy the caller's map and use putIfAbsent for the default input registration, so user-registered strategies survive cloneState()/snapShot() and the shared map is never aliased.

Reproduction for the issue reported upstream in alibaba/spring-ai-alibaba#4999.

@yuluo-yx yuluo-yx left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM thanks for you

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.

2 participants