From b0a9ca7daa38f7d354e4d2078f3b363bf9e70ae9 Mon Sep 17 00:00:00 2001 From: Sydney Runkle Date: Tue, 28 Apr 2026 17:49:37 -0400 Subject: [PATCH] fix(channels): DeltaChannel subclass safety and order-independent Overwrite MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit copy() and from_checkpoint() hardcoded DeltaChannel instead of self.__class__, breaking subclasses. Now mirrors the BinaryOperatorAggregate pattern: self.__class__(self.operator) with explicit typ/key assignment. update() applied non-overwrite values that arrived before an Overwrite in the sequence, then discarded them when the Overwrite fired — order-dependent behaviour in a method whose contract says order is arbitrary. Now pre-scans for an Overwrite and applies only it (or folds all values normally if none). Co-Authored-By: Claude Sonnet 4.6 (1M context) --- libs/langgraph/langgraph/channels/delta.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/libs/langgraph/langgraph/channels/delta.py b/libs/langgraph/langgraph/channels/delta.py index bb7945bb6..7be783013 100644 --- a/libs/langgraph/langgraph/channels/delta.py +++ b/libs/langgraph/langgraph/channels/delta.py @@ -145,19 +145,21 @@ class DeltaChannel(Generic[Value], BaseChannel[Any, Any, Any]): def update(self, values: Sequence[Any]) -> bool: if not values: return False - seen_overwrite = False - for value in values: - is_overwrite, _ = _get_overwrite(value) - if is_overwrite: - if seen_overwrite: + overwrite_idx: int | None = None + for i, v in enumerate(values): + is_ow, _ = _get_overwrite(v) + if is_ow: + if overwrite_idx is not None: msg = create_error_message( message="Can receive only one Overwrite value per super-step.", error_code=ErrorCode.INVALID_CONCURRENT_GRAPH_UPDATE, ) raise InvalidUpdateError(msg) - seen_overwrite = True - elif seen_overwrite: - continue + overwrite_idx = i + if overwrite_idx is not None: + self.value = self._apply_write(self.value, values[overwrite_idx]) + return True + for value in values: self.value = self._apply_write(self.value, value) return True