From 1cb057e6bcc1fc643aa88e3b105b7fb2eb40610f Mon Sep 17 00:00:00 2001 From: Sydney Runkle Date: Fri, 17 Apr 2026 14:54:58 -0400 Subject: [PATCH] fix(checkpoint/memory): warn on broken diff chain, guard against cycles - Add logger.warning when a mid-chain blob is missing (fixes silent truncation bug) - Add cycle guard to prevent infinite loops on corrupt blob stores - Fix type annotation on diff_channels from dict[str, Any] to dict[str, str] Co-Authored-By: Claude Sonnet 4.6 --- .../langgraph/checkpoint/memory/__init__.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/libs/checkpoint/langgraph/checkpoint/memory/__init__.py b/libs/checkpoint/langgraph/checkpoint/memory/__init__.py index 17829b7a0..c0e646ae5 100644 --- a/libs/checkpoint/langgraph/checkpoint/memory/__init__.py +++ b/libs/checkpoint/langgraph/checkpoint/memory/__init__.py @@ -126,7 +126,7 @@ class InMemorySaver( from langgraph.checkpoint.base import DiffChainValue channel_values: dict[str, Any] = {} - diff_channels: dict[str, Any] = {} + diff_channels: dict[str, str] = {} for k, v in versions.items(): kk = (thread_id, checkpoint_ns, k, v) @@ -142,9 +142,24 @@ class InMemorySaver( chain_deltas: list[list[Any]] = [] base: list[Any] | None = None version: str | None = current_version + visited: set[str] = set() while version is not None: + if version in visited: + logger.warning( + "DiffChannel chain cycle detected at version %r for channel %r; breaking", + version, + k, + ) + break + visited.add(version) kk = (thread_id, checkpoint_ns, k, version) if kk not in self.blobs: + logger.warning( + "DiffChannel chain is broken: blob for channel %r version %r not found; " + "partial history will be returned", + k, + version, + ) break vv = self.blobs[kk] if vv[0] == "diff":