Skip to content

fix(langgraph): replay task results on nested resume - #2679

Open
Serhiy Bzhezytskyy (serhiy-bzhezytskyy) wants to merge 1 commit into
langchain-ai:mainfrom
serhiy-bzhezytskyy:fix/nested-resume-task-replay-2667
Open

fix(langgraph): replay task results on nested resume#2679
Serhiy Bzhezytskyy (serhiy-bzhezytskyy) wants to merge 1 commit into
langchain-ai:mainfrom
serhiy-bzhezytskyy:fix/nested-resume-task-replay-2667

Conversation

@serhiy-bzhezytskyy

Copy link
Copy Markdown

Summary

Fixes #2667

A task() that completed before an interrupt() records its result as a pending write, so resuming should reuse it. It does when the graph runs standalone. Composed as a subgraph, the body runs again on every resume — one duplicated side effect per resume, and task() is the wrapper for the non-idempotent step.

Root cause

A subgraph's loop receives checkpoint_id as a key in its configurable, and skipDoneTasks tests for the key rather than a value:

// libs/langgraph-core/src/pregel/loop.ts
const skipDoneTasks = config.configurable
  ? !("checkpoint_id" in config.configurable)
  : true;

So it is false on every nested run and the two sites that hand a recorded result to a task never fire — _matchWrites() for a pushed Call task, and the pending-writes loop in tick(). task.writes stays empty, the short-circuit in runner.ts that returns a recorded RETURN write cannot run, and the body executes again.

The predicate itself is load-bearing. Python fixed the same bug in langchain-ai/langgraph#6161 by conditioning the derivation on resuming inside a nested graph; ported here, that shape fails the Direct-to-subgraph time travel group in time_travel.test.ts, and making the check test the value instead of the key fails seven tests in the same file.

Changes

  • pregel/loop.ts: gate applying pending writes on isTimeTraveling — the predicate _first() already computes and already uses to drop stale RESUME writes — instead of skipDoneTasks, whose derivation is untouched. isReplaying still derives from it, so it still reads true for every nested run.
  • tests/nested-resume-task-replay.test.ts: standalone resume (the control), nested resume, and an explicit checkpoint_id replay, which must still re-run the task.

Test plan

  • the nested-resume case fails expected 2 to be 1 with the change stashed
  • pnpm lint, pnpm format:check and the langgraph-core suite pass locally, including the time-travel suites
  • Changeset for @langchain/langgraph

Reproduces on @langchain/langgraph 1.4.9, on main, and on Python langgraph 1.2.10 — langchain-ai/langgraph#6161 fixed it there, and the later rename of skip_done_tasks to is_replaying dropped the nested-resume condition.

A subgraph's loop receives `checkpoint_id` as a key in its `configurable`,
so `skipDoneTasks` was false for every nested run and the recorded pending
writes were never applied to the replayed tasks. A `task()` that completed
before an `interrupt()` therefore executed its body again on resume — a
duplicated side effect, since `task()` is the wrapper for the
non-idempotent step. The standalone case was unaffected.

Gate applying pending writes on the existing `isTimeTraveling` predicate,
which `_first()` already computes and which distinguishes replaying a
specific checkpoint from resuming an interrupt. `skipDoneTasks` is left
untouched, so the subgraph time-travel behaviour it guards is unchanged.
@changeset-bot

changeset-bot Bot commented Aug 11, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 6060c05

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@langchain/langgraph Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown

Open in StackBlitz

@langchain/langgraph-checkpoint

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint@2679

@langchain/langgraph-checkpoint-mongodb

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-mongodb@2679

@langchain/langgraph-checkpoint-postgres

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-postgres@2679

@langchain/langgraph-checkpoint-redis

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-redis@2679

@langchain/langgraph-checkpoint-sqlite

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-sqlite@2679

@langchain/langgraph-checkpoint-validation

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-checkpoint-validation@2679

create-langgraph

npm i https://pkg.pr.new/langchain-ai/langgraphjs/create-langgraph@2679

@langchain/langgraph-api

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-api@2679

@langchain/langgraph-cli

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-cli@2679

@langchain/langgraph

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph@2679

@langchain/langgraph-cua

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-cua@2679

@langchain/langgraph-supervisor

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-supervisor@2679

@langchain/langgraph-swarm

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-swarm@2679

@langchain/langgraph-ui

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-ui@2679

@langchain/langgraph-sdk

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/langgraph-sdk@2679

@langchain/angular

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/angular@2679

@langchain/react

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/react@2679

@langchain/svelte

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/svelte@2679

@langchain/vue

npm i https://pkg.pr.new/langchain-ai/langgraphjs/@langchain/vue@2679

commit: 6060c05

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.

task() is re-executed on resume when the graph runs as a subgraph (reused fine when standalone)

1 participant