Files
langgraph/libs
Sydney RunkleandGitHub f78892d462 fix: replay bug, direct to subgraphs (#7115)
## Summary

Fixes two bugs when time-traveling to a subgraph checkpoint
(`graph.invoke(None, sub_config)`):

1. **Wrong checkpoint loaded:** The old `__enter__` checked
`replay_state` before explicit `checkpoint_id` for nested graphs. When
time-traveling, `checkpoint_map` resolves a specific checkpoint_id for
the target subgraph, but `replay_state.get_checkpoint()` would find an
*earlier* checkpoint (via `list(before=parent_ckpt_id)`), causing the
subgraph to re-execute from the beginning (step_a, ask_1, etc. all
re-ran). Fix: check for explicit `checkpoint_id` first.

2. **Stale RESUME writes kept:** The parent sets `RESUMING=True` on
subgraph configs — it can't distinguish time-travel from normal resume.
The subgraph sees `RESUMING=True` and preserves old RESUME writes, so
`interrupt()` returns cached answers instead of re-firing. Fix: check
whether the subgraph's own namespace appears in `checkpoint_map` (it
only does during time-travel — normally the map only has ancestor
entries). When present, force-strip RESUME writes.

## Test plan

Tests cover time travel (replay + fork) at different interrupt points
for both single-nested and double-nested subgraphs, including cases
where the middle subgraph itself has interrupts. All tests have sync and
async variants.

### Single nested (2 levels: parent → executor subgraph)
- `test_subgraph_time_travel_to_first_interrupt` — time travel to 1st
interrupt checkpoint; verifies step_a doesn't re-run, ask_1 re-fires
(replay + fork)
- `test_subgraph_time_travel_to_second_interrupt` — time travel to 2nd
interrupt checkpoint; verifies step_a and ask_1 don't re-run, ask_2
re-fires (replay + fork)
- `test_subgraph_time_travel_after_completion` — replay from final
parent checkpoint after full completion; verifies no nodes re-run and
all state values preserved

### Double nested (3 levels: parent → outer → inner subgraph)
- `test_3_levels_deep_time_travel_to_first_interrupt` — time travel to
innermost checkpoint at 1st interrupt (replay + fork)
- `test_3_levels_deep_time_travel_to_second_interrupt` — time travel to
innermost checkpoint at 2nd interrupt (replay + fork)
- `test_3_levels_deep_time_travel_to_middle_subgraph` — time travel to
middle-level subgraph checkpoint (replay + fork)
- `test_3_levels_deep_middle_has_interrupts` — middle subgraph has its
own `interrupt()` calls (in `pre` node) plus an inner subgraph with
interrupts; time travel to middle checkpoint at each interrupt point
(replay + fork at both the middle's own interrupt and the inner's
interrupt)
2026-03-11 21:59:45 +00:00
..
2026-03-10 12:41:30 +00:00