You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
BinaryOperatorAggregate stores the Overwrite wrapper itself when it is the first write to an empty channel #9017
LangGraph main at 230927fb3a9ac9b2893a30322b4dfea7cdea9a8f (the update implementation is unchanged in the latest release)
When the first write to an empty BinaryOperatorAggregate channel is an Overwrite, the empty-channel branch assigns values[0] verbatim, so the channel value — and the checkpointed state — holds the Overwrite wrapper itself instead of its payload. Every consumer that later reads the state then sees the wrapper object, typically failing downstream with something like TypeError: 'Overwrite' object is not subscriptable.
Union-typed channels (e.g. Annotated[dict | None, reducer]) have no constructible default, so they start out MISSING; a replace-style write into a fresh thread or a never-written channel is exactly this path.
The same write is handled correctly once the channel has a value:
DeltaChannel.update also unwraps in the same situation, so only the empty-channel first-write branch of BinaryOperatorAggregate skips the _get_overwrite check. Expected: Overwrite(x) as a first write stores x, matching the seeded-channel behavior.
Error Message and Stack Trace (if applicable)
The wrapper silently persists into the checkpointed state; the failure surfaces later at the consumer, e.g. TypeError: 'Overwrite' object is not subscriptable when the persisted state is read back.
I reproduced this on current main: when a BinaryOperatorAggregate channel is still empty, the first write that wraps the value in Overwrite stores the wrapper object itself. Seeded / later paths already unwrap via _get_overwrite, so consumers later hit a TypeError when they expect the bare value.
Proposed fix: in libs/langgraph/langgraph/channels/binop.py, unwrap Overwrite on that empty-channel first write the same way the seeded path does, plus a small test_channels regression covering empty vs seeded first writes.
Could a maintainer please assign this issue to me (leilei3167) so I can open a PR under the repo process? Happy to take it from there.
I'd like to fix this one. Confirmed on main: BinaryOperatorAggregate.update seeds the channel with �alues[0] verbatim when the channel is empty, so a first-write Overwrite stores the wrapper object itself. Fix mirrors the single-value-channel unwrap in #9019 (_get_overwrite on the seed), with a regression test. Could a maintainer assign this to me so I can open the PR?
PR opened: #9037 - BinaryOperatorAggregate.update now unwraps an Overwrite seed via _get_overwrite on the empty-channel branch (mirroring the #9019 single-value fix), with a regression test asserting the channel holds the payload dict, not the wrapper. Existing est_channels.py all pass.
Sydney Runkle (@sydney-runkle) could you assign this one too? My PR #9037 was auto-closed pending assignment; once assigned the bot will reopen it automatically.
Activity
leilei3167 commentedon Sep 21, 2026
I reproduced this on current main: when a
BinaryOperatorAggregatechannel is still empty, the first write that wraps the value inOverwritestores the wrapper object itself. Seeded / later paths already unwrap via_get_overwrite, so consumers later hit aTypeErrorwhen they expect the bare value.Proposed fix: in
libs/langgraph/langgraph/channels/binop.py, unwrapOverwriteon that empty-channel first write the same way the seeded path does, plus a smalltest_channelsregression covering empty vs seeded first writes.Could a maintainer please assign this issue to me (
leilei3167) so I can open a PR under the repo process? Happy to take it from there.Rainmemery commentedon Sep 21, 2026
I'd like to fix this one. Confirmed on main: BinaryOperatorAggregate.update seeds the channel with �alues[0] verbatim when the channel is empty, so a first-write Overwrite stores the wrapper object itself. Fix mirrors the single-value-channel unwrap in #9019 (_get_overwrite on the seed), with a regression test. Could a maintainer assign this to me so I can open the PR?
Rainmemery commentedon Sep 21, 2026
PR opened: #9037 - BinaryOperatorAggregate.update now unwraps an Overwrite seed via _get_overwrite on the empty-channel branch (mirroring the #9019 single-value fix), with a regression test asserting the channel holds the payload dict, not the wrapper. Existing est_channels.py all pass.
Rainmemery commentedon Sep 26, 2026
Sydney Runkle (@sydney-runkle) could you assign this one too? My PR #9037 was auto-closed pending assignment; once assigned the bot will reopen it automatically.