Thrive AI Editorial
Thrive AI Editorial · AI-assisted worked example, not a member conversation.
2026-10-07
Diagnose the address before changing the prompt
A checkpoint is stored under a runtime identity. Starting the next request with a new thread identifier selects a different history. Reusing an identifier for unrelated conversations selects the same history. Neither failure is fixed by asking the model to remember harder.
LangGraph distinguishes a checkpointer, which persists thread-scoped graph state, from a store, which holds application-defined data across threads. An in-memory checkpointer loses state when its process ends. A durable backend solves that persistence problem, but does not decide which authenticated account may access a thread.
authenticated account -> authorized conversation -> stable thread identity
-> durable checkpoint backend
Reproduce the addressing mistake
Python 3, standard library only. This is an original key-scope simulation, not a LangGraph integration. Both tenant and conversation are application-controlled here.
from copy import deepcopy
checkpoints = {}
def save(account, conversation, state):
checkpoints[(account, conversation)] = deepcopy(state)
def load(account, conversation):
return deepcopy(checkpoints.get((account, conversation)))
save("team-a", "case-17", {"step": "awaiting-review"})
assert load("team-a", "new-random-id") is None
assert load("team-b", "case-17") is None
resumed = load("team-a", "case-17")
assert resumed == {"step": "awaiting-review"}
resumed["step"] = "local-change"
assert load("team-a", "case-17")["step"] == "awaiting-review"
print("new thread: empty")
print("other account: isolated")
print("same thread:", load("team-a", "case-17")["step"])
Expected output:
new thread: empty
other account: isolated
same thread: awaiting-review
Apply the diagnosis to a real graph
Inspect the thread identifier on the initial request and the resume request. Confirm they refer to the same authorized conversation. Inspect which checkpointer is actually attached to the compiled graph, not merely which package is installed. Verify database connectivity and persistence across a process restart.
Do not expose arbitrary checkpoint lookup by accepting a client-supplied account identifier. Resolve the account from authentication, check conversation ownership, and only then resolve the runtime thread key. A UUID is an identifier, not an access-control decision.
Our tuple-key example does not prescribe the internal key format of a LangGraph backend. Keep application authorization separate from provider-specific configuration. Store cross-conversation facts deliberately rather than accidentally reusing one conversation identifier for every user.
Verification checklist
Resume the same conversation after restarting the worker. Start a genuinely new conversation. Attempt access using another authenticated account. Resume two conversations concurrently. Record the runtime version, checkpoint backend, thread identity and checkpoint identifier without recording sensitive prompt text.
The expected invariants are state continuity for the same authorized conversation and isolation across distinct conversations. Persistence does not guarantee that tools with external side effects execute only once; those need their own idempotency boundary.
Source checked 2026-10-08: LangGraph persistence. The thread/checkpointer/store distinction and in-memory limitation come from that documentation. Account scoping and the executable simulation are our own AI-assisted explanation, not a tested framework configuration.
Sign in to vote or contribute. Scores count actual member votes, not editorial endorsements.