Files
langgraph/libs
Elior Nataf LackritzandGitHub 1b84cb0873 fix(langgraph): keep DeltaChannel counters on every update_state path (#9142)
`update_state` saves `counters_since_delta_snapshot` on the checkpoint
it creates, except on three paths: `update_state(None, as_node=END)`,
`as_node="__input__"` and `as_node="__copy__"`. Those save their
checkpoint without it, so the next checkpoint restarts every delta
channel's counters from zero:

```
before update_state(None, END): [6, 9]
after it:                       (none)
after the next run:             [2, 3]   (with this change: [6, 10], then [8, 13])
```

The counters decide when a channel snapshots, so after any of these
updates the next snapshot comes later than `snapshot_frequency` and
reads walk further back. A thread driven only by these updates never
snapshots, so its walk keeps growing. The field's docstring says it is
absent only on threads that don't use delta channels.

## Fix

`END` and `__input__` build their checkpoint with
`create_checkpoint_plan_for_update_state_api`, the plan an update as a
node already uses, which now takes the checkpoint's `source`. The
counters advance by one superstep (`END` counts the channels its
absorbed writes touched as updated), and a channel that reaches its
bound snapshots right there. `__copy__` saves a checkpoint with the same
values on the same parent, so it keeps the copied checkpoint's counters.

## Tests

`test_delta_channel_supersteps_bound.py`, sync and async:
- each of the three paths saves the base's counters advanced by one
superstep (or unchanged for `__copy__`).
- `END` and `__input__` snapshot a channel that reaches its supersteps
bound.

All 10 fail on main.
2026-10-05 13:04:30 -04:00
..
2026-10-05 12:13:11 -04:00