Home / Articles

LangGraph Persistence: Checkpoints That Unlock Resume and HITL

LangGraph persistence explained: how checkpoints store state per thread so interrupts, human in the loop approval, retries and time-travel debugging work.

Publisher: REACTAPP.TOP7 min read1608 words

This article is published in English.

Sources: LangGraph Persistence: The Feature That Makes Everything Else Possible

This walkthrough rebuilds an operable path for: LangGraph Persistence: The Feature That Makes Everything Else Possible. Focus on contracts, checks, and code you can drop into a repo without guessing intent. For Overview, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility.

A Quick Recap of What We Know So Far

For A Quick Recap of What We Know So Far, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Checkpoint after expensive steps so retries do not re-bill the same model call.

What Persistence Actually Is

For What Persistence Actually Is, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Record timings and cost next to functional results. Visibility early prevents surprise bills in shared environments. Checkpoint after expensive steps so retries do not re-bill the same model call.

How It Actually Works: Checkpointers

For How It Actually Works: Checkpointers, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit. Checkpoint after expensive steps so retries do not re-bill the same model call. For How It Actually Works: Checkpointers, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility.

Threading: Keeping Multiple Executions Separate

For Threading: Keeping Multiple Executions Separate, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Persistence turns demos into systems: threads, humans-in-the-loop, and time-travel debugging depend on it.

The Code (It’s Simpler Than You’d Expect)

For The Code (It’s Simpler Than You’d Expect), define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Record timings and cost next to functional results. Visibility early prevents surprise bills in shared environments. Persistence turns demos into systems: threads, humans-in-the-loop, and time-travel debugging depend on it.

from langchain_openai import ChatOpenAI
from langgraph.graph import StateGraph
from langgraph.checkpoint.memory import InMemorySaver
from typing import TypedDict

llm = ChatOpenAI()

class JokeState(TypedDict):
    topic: str
    joke: str
    explanation: str

def generate_joke(state: JokeState):
    response = llm.invoke(f"Generate a joke on the topic: {state['topic']}")
    return {"joke": response.content}

def generate_explanation(state: JokeState):
    response = llm.invoke(f"Explain this joke: {state['joke']}")
    return {"explanation": response.content}

# Build graph
graph = StateGraph(JokeState)
graph.add_node("generate_joke", generate_joke)
graph.add_node("generate_explanation", generate_explanation)
graph.add_edge("__start__", "generate_joke")
graph.add_edge("generate_joke", "generate_explanation")
graph.add_edge("generate_explanation", "__end__")

# Add persistence - this is the key line
checkpointer = InMemorySaver()
workflow = graph.compile(checkpointer=checkpointer)
config = {"configurable": {"thread_id": "1"}}

result = workflow.invoke(
    {"topic": "pizza"},
    config=config
)
# Get the final state
workflow.get_state(config)

# Get ALL intermediate states
workflow.get_state_history(config)
config_2 = {"configurable": {"thread_id": "2"}}
result_2 = workflow.invoke({"topic": "pasta"}, config=config_2)

What You Can Build With This

For What You Can Build With This, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit. Persistence turns demos into systems: threads, humans-in-the-loop, and time-travel debugging depend on it. For What You Can Build With This, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility.

1. Short-Term Memory in Chatbots

For 1. Short-Term Memory in Chatbots, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.

2. Fault Tolerance

For 2. Fault Tolerance, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Record timings and cost next to functional results. Visibility early prevents surprise bills in shared environments. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts.

import time

def step_two(state):
    print("Step 2 running...")
    time.sleep(30)  # simulating a slow operation
    return {"step_two": "done"}
workflow.invoke(None, config=config)

3. Human-in-the-Loop

For 3. Human-in-the-Loop, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit. Keep graph state flat and typed. Nested blobs hide which node wrote which field and break resume after interrupts. For 3. Human-in-the-Loop, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility.

4. Time Travel

For 4. Time Travel, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Treat this stage as a contract between inputs and validated outputs. Name the artifacts, define success checks, and refuse silent partial completion. Checkpoint after expensive steps so retries do not re-bill the same model call.

# Go back to a specific checkpoint and change the topic
workflow.update_state(
    {"configurable": {"thread_id": "1", "checkpoint_id": "<checkpoint_id>"}},
    {"topic": "samosa"}
)

# Re-run from that point
workflow.invoke(None, config={"configurable": {"thread_id": "1", "checkpoint_id": "<new_checkpoint_id>"}})

The Bigger Picture

For The Bigger Picture, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Record timings and cost next to functional results. Visibility early prevents surprise bills in shared environments. Checkpoint after expensive steps so retries do not re-bill the same model call.

Structured Learning Beyond Blogs

For Structured Learning Beyond Blogs, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Keep configuration outside application code. Environment files, secret stores, and feature flags belong in one place operators can audit. Checkpoint after expensive steps so retries do not re-bill the same model call.

Courses

For Courses, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state. Document the happy path and the recovery path together. Retries and dead-letter handling are part of the product. Checkpoint after expensive steps so retries do not re-bill the same model call.

Operational checklist

For Operational checklist, define the inputs, the owner of the step, and the exit criteria before changing code. Operators should be able to re-run the step from a known checkpoint without guessing hidden state.

Document the happy path and the recovery path together. Retries and dead-letter handling are part of the product.

Persistence turns demos into systems: threads, humans-in-the-loop, and time-travel debugging depend on it.

Add a smoke test for the critical path in CI with fixtures when budgets allow.

Prefer small, testable units over sprawling scripts. When a step fails, the failure should point at a single responsibility.

Persistence turns demos into systems: threads, humans-in-the-loop, and time-travel debugging depend on it.

Before promoting the stack, freeze versions, capture a golden transcript for the critical path, and confirm rollback steps. Shared environments need rate limits, tenancy checks, and a clear owner for secret rotation.