Independent XBSTACK reproduction of the write-ownership gap discussed in LangGraph issue #8796 and the corresponding LangChain Forum thread.
Does a PostgreSQL-backed LangGraph Checkpointer make two legitimate workers safe concurrent writers for the same thread_id?
This experiment tests one narrow production boundary:
- Worker A starts a plain
MessagesStategraph and is frozen inside its second deterministic model call. - Worker B resumes the same
thread_idwithinvoke(None, config)and finishes. - Worker A is thawed and continues writing from the checkpoint that was current before B took over.
- The script queries the real
checkpointstable and checks whether the parent checkpoint has more than one child, whether B returned success, and whether B's committed checkpoints become unreachable from the final tip.
No external model/API is used. The model is scripted and deterministic. PostgreSQL is provided locally by pgserver; Docker is not required.
- Test date: 2026-09-08
- Python: 3.12
langgraph: 1.2.11langgraph-checkpoint: 4.2.0langgraph-checkpoint-postgres: 3.1.2langchain-core: 1.6.1psycopg: 3.3.4pgserver: 0.1.4
uv venv --python 3.12 .venv
VIRTUAL_ENV=$PWD/.venv uv pip install -r requirements.txt
.venv/bin/python repro.py
.venv/bin/python fencing_control.pyrepro.py writes results/verification.json. A successful reproduction ends with:
"result": "REPRODUCED"
The expected evidence is:
- both worker processes exit successfully;
- the checkpoint chain contains a fork whose parent is the checkpoint that was the tip when Worker A was frozen;
- Worker B returns
final answer from Bto its caller; - after Worker A resumes, at least one checkpoint committed during B's run is no longer reachable from the final tip;
- the final thread tip contains Worker A's terminal answer instead.
fencing_control.py demonstrates the minimum application-level ownership concept with a versioned owner row:
- Worker A starts with ownership version 1;
- Worker B atomically claims version 2;
- Worker A's stale version 1 is rejected before it is allowed to write;
- Worker B remains the current owner.
This control is intentionally limited. It is not an upstream LangGraph fix and it is not atomic with PostgresSaver writes. A production fence must be integrated at the write boundary or enforced by an orchestration layer that can guarantee a stale worker cannot pass the ownership check and then write later.
This repository does not prove that every LangGraph version or every saver behaves identically. It tests the pinned versions above, on one machine, with two OS processes and one embedded PostgreSQL instance. It does not model a multi-machine network partition, PgBouncer transaction pooling, clock skew, or external tool side effects.
The experiment is designed to verify the narrow statement used in the XBSTACK production guides: database transaction consistency is not the same thing as same-thread write ownership.
- LangGraph issue #8796: langchain-ai/langgraph#8796
- LangChain Forum discussion: https://forum.langchain.com/t/a-checkpointer-that-accepts-two-concurrent-writers-still-scores-conformance-level-full/4460
- Original upstream reproduction that motivated this independent test: https://github.com/bharatnpti/langgraph-write-ownership
- Supervisor / Worker handoff: https://www.xbstack.com/en/ai/langgraph-supervisor-worker-handoff/?utm_source=github&utm_medium=referral&utm_campaign=langgraph_same_thread_write_ownership&utm_content=repository_readme
- Memory / Checkpointing: https://www.xbstack.com/en/ai/langgraph-memory-checkpointing-production-agents/?utm_source=github&utm_medium=referral&utm_campaign=langgraph_same_thread_write_ownership&utm_content=related_1
The failure scenario follows the same high-level freeze/takeover/thaw idea documented by the upstream issue and bharatnpti/langgraph-write-ownership, which is MIT licensed. This XBSTACK fixture is a smaller, plain-MessagesState implementation written to independently verify the checkpoint-chain ownership claim without Deep Agents or a real provider.