mirror of
https://github.com/langchain-ai/langgraph.git
synced 2026-09-23 18:15:08 +02:00
Stage 1 of the sqlite delta history filtered `checkpoint_id <= target` and streamed `ORDER BY checkpoint_id DESC`. Both encode an extra assumption: that every child's checkpoint id sorts above its parent's. Ancestry is defined by `parent_checkpoint_id`, and nothing in the contract requires ids to be monotonic. A parent whose id sorted above its child's was dropped from the stream, so its stored value and its writes were lost with no error raised. Removing the range filter alone would not help: in DESC order that parent arrives before the target, so the walk passes it before it has started. Replace it with a recursive CTE anchored at the target that follows `parent_checkpoint_id`. Rows now arrive in walk order, so the off-path skip and parent tracking in `step_walk_with_row` are dead and removed, and the query reads only true ancestors instead of every row at or below the target. Following pointers can loop where a bounded id scan could not, and a loop is reachable through `put` alone: it writes with `INSERT OR REPLACE`, so re-putting an existing checkpoint id under a descendant's config repoints that checkpoint at its own descendant. The walk therefore stops on a repeated checkpoint id. sqlite yields recursive rows lazily, so abandoning the cursor ends the recursion. Postgres needs no equivalent change: it pages the whole thread without an id bound and follows parent pointers in Python, and its upsert never rewrites `parent_checkpoint_id`, so it cannot form this loop. Fixes #8550 Co-authored-by: lylelllll <59271327+lylelllll@users.noreply.github.com>