LangChain released langgraph==1.2.13 on 2026-10-05 at 17:51 UTC. The release forks before replaying a superseded update checkpoint, keeps update_state writes on older checkpoints out of existing sibling branches, and preserves DeltaChannel counters across all update paths. Latest-state get_state() reads no longer report interrupts answered by finished tasks.
Key facts
- GitHub marked langgraph==1.2.13 released on 2026-10-05 at 17:51 UTC; PyPI lists the first 1.2.13 upload at 17:51:20 UTC. The Git tag points to commit
2d942085e214ef6b99b6f54ed4d544a7c7c5ac56. - Pull request #9170 forces a fork checkpoint before replaying an update or fork checkpoint that has been superseded by newer thread checkpoints.
- Pull request #9165 prevents
update_stateon an older checkpoint from storing writes on the base, keepingDeltaChannelancestor replays from bleeding into sibling branches. - Pull request #9142 preserves
counters_since_delta_snapshotonupdate_state(None, as_node=END),as_node="__input__", andas_node="__copy__". - Pull request #9103 unifies task-status resolution across execution, state reads, and resume checks. Latest-state
get_state()omits interrupts belonging to finished tasks. - Pull request #9141 stops
DeltaChannelfrom walking history on channels that were never written, avoiding SQLitetoo many SQL variablesfailures on a 33,000-step thread.
Why older checkpoint updates leaked into sibling branches
In LangGraph, DeltaChannel avoids saving full state blobs on every step. Instead, it stores a sentinel marker in checkpoint blobs and reconstructs channel values by walking backwards through ancestor checkpoints and replaying their stored writes through a reducer function.
Prior to langgraph==1.2.13, modifying state on an older checkpoint created cross-branch contamination. When a caller used update_state on a past checkpoint, LangGraph wrote pending writes directly to that addressed checkpoint. Because DeltaChannel reconstructs values by replaying ancestor writes, any existing branch that had already branched off that checkpoint picked up those newly patched writes upon reloading:
Original branch before edit: ['in-1', 'first-out', 'in-2', 'second-out']
Original branch after edit: ['in-1', 'first-out', 'patched', 'in-2', 'second-out']In pull request #9165, which resolved issue #8551, LangGraph changed how updates to older checkpoints are saved. When update_state addresses a checkpoint that is not the latest on the thread, LangGraph writes a full snapshot blob for the updated delta channels onto the new checkpoint. Crucially, it stores no writes on the addressed base checkpoint. The edited branch reads its new snapshot, while preexisting descendant branches replay their original, unmodified history.
How LangGraph 1.2.13 forks superseded checkpoints before replay
In LangGraph versions prior to 1.2.13, a related bug affected replaying older update checkpoints. When a caller replayed an execution from a specific checkpoint_id, LangGraph checked the metadata source tag. If the source was "update" or "fork", LangGraph previously skipped creating a new fork checkpoint under the assumption that an update checkpoint was already its own branch.
When a thread had continued past an update checkpoint, that checkpoint already had children. Skipping the fork meant that the replay's new writes—such as Command(update=...) or Command(goto=...)—landed directly on the old checkpoint. Sibling branches that grew from that checkpoint then replayed those writes during their own ancestor walks:
plain channel: ['in1', 'a', 'b', 'edit', 'in2', 'a', 'b']
DeltaChannel: ['in1', 'a', 'b', 'edit', 'b', 'in2', 'a', 'b']Pull request #9170 introduced checkpoint_superseded and acheckpoint_superseded helpers in _checkpoint.py. When loading a checkpoint addressed by checkpoint_id, LangGraph queries the checkpointer for the latest thread checkpoint:
def checkpoint_superseded(
saver: BaseCheckpointSaver, config: RunnableConfig, saved: CheckpointTuple
) -> bool:
if not config[CONF].get(CONFIG_KEY_CHECKPOINT_ID):
return False
latest = saver.get_tuple(
patch_configurable(config, {CONFIG_KEY_CHECKPOINT_ID: None})
)
return latest is not None and latest.checkpoint["id"] != saved.checkpoint["id"]If the addressed update or fork checkpoint is superseded, LangGraph saves a fork checkpoint before storing any replay writes. The runner retains Command writes in memory, snapshots written delta channels on the fork, and writes nothing to the base checkpoint. Sibling branches remain insulated.
One intentional boundary remains in pull request #9170: Command(resume=...) addressed at a superseded checkpoint does not fork. Forking changes task identifiers, which would invalidate interrupt IDs and resume-map key hashes computed from the caller's state read.
Preserving DeltaChannel counters across update_state paths
In LangGraph, DeltaChannel bounds its replay walk depth using periodic snapshots. Snapshot frequency is governed by two limits: a per-channel update count (default snapshot_frequency=1000) and supersteps elapsed since the last snapshot (bounded by DELTA_MAX_SUPERSTEPS_SINCE_SNAPSHOT=5000).
Checkpoints track these cadences in a metadata dictionary named counters_since_delta_snapshot. In prior versions, three specific update paths dropped this metadata:
update_state(None, as_node=END)(clearing state or absorbing pending writes)- Updates using
as_node="__input__" - Updates using
as_node="__copy__"
When these paths omitted the metadata, subsequent checkpoints reset delta counters to zero. On threads driven primarily by state resets or input updates, delta channels never reached their snapshot threshold, causing ancestor history walks to grow without bound.
Pull request #9142 routes END and __input__ updates through create_checkpoint_plan_for_update_state_api(..., source=...). Counters advance by one superstep, immediate snapshots trigger if bounds are crossed, and __copy__ preserves the parent checkpoint's existing counter map.
Answered interrupts removed from get_state
Before langgraph==1.2.13, execution, state snapshots, and resume checks interpreted task status separately. When one of several interrupted tasks finished after a resume, the latest-state get_state() read still reported its answered interrupt as pending.
Pull request #9103 fixed issue #6956 and issue #8579 by introducing centralized status tracking via read_task_statuses(). Version 1.2.13 filters interrupts belonging to finished tasks from latest-state reads. History and explicit-checkpoint reads still show the last recorded interrupt for each task. When a resumed task finishes without generating output, LangGraph writes a NO_WRITES sentinel so it is not run again. Command(resume=value) without an explicit interrupt ID raises an error whenever more than one interrupt is pending on the thread.
Unwritten DeltaChannel ancestor walks avoided
In applications containing delta channels that receive no writes during a thread, such as an unused tool or scratchpad channel, channels_from_checkpoint previously queried checkpointer history back to the thread's root. On SQLite checkpointers, a 33,000-step thread could fail with OperationalError: too many SQL variables during the ancestor walk.
Pull request #9141 restricts ancestor walks to channels with an explicit version recorded at the checkpoint. A channel lacking a version hydrates immediately as empty without asking the saver for history.
Sources
- Release langgraph==1.2.13: https://github.com/langchain-ai/langgraph/releases/tag/1.2.13 (read 2026-10-06)
- PyPI package metadata for langgraph 1.2.13: https://pypi.org/pypi/langgraph/json (read 2026-10-06)
- PR #9170: Fork before replaying an update checkpoint the thread moved past: https://github.com/langchain-ai/langgraph/pull/9170 (read 2026-10-06)
- PR #9165: Keep an update_state on an older checkpoint out of its other branches: https://github.com/langchain-ai/langgraph/pull/9165 (read 2026-10-06)
- PR #9142: Keep DeltaChannel counters on every update_state path: https://github.com/langchain-ai/langgraph/pull/9142 (read 2026-10-06)
- PR #9103: Stop reporting answered interrupts in get_state: https://github.com/langchain-ai/langgraph/pull/9103 (read 2026-10-06)
- PR #9141: Don't walk history for a DeltaChannel that was never written: https://github.com/langchain-ai/langgraph/pull/9141 (read 2026-10-06)
- DeltaChannel API reference: https://reference.langchain.com/python/langgraph/channels/delta/DeltaChannel (read 2026-10-06)
Last verified: 2026-10-06.