Skip to content
Built by Postindustria. We help teams build agentic production systems.

Checkpoint Resume

When you run a pipeline with a checkpointer (MemorySaver, SqliteSaver, PostgresSaver), neograph automatically handles three resume scenarios:

If a pipeline crashes mid-execution, re-run with the same thread_id. Completed nodes are skipped; execution resumes from the failure point.

from langgraph.checkpoint.memory import MemorySaver
checkpointer = MemorySaver()
graph = compile(pipeline, checkpointer=checkpointer)
config = {"configurable": {"thread_id": "my-pipeline-001"}}
# First run — crashes at node 4
run(graph, input={"node_id": "BR-001"}, config=config)
# Second run — same config, resumes from node 4
run(graph, input={"node_id": "BR-001"}, config=config)

This is the key feature. If you change a node’s output model between runs, neograph detects the change and re-executes only the affected nodes. Expensive upstream work is preserved.

# Run 1: full pipeline (A -> B -> C -> D)
graph_v1 = compile(pipeline_v1, checkpointer=checkpointer)
run(graph_v1, input={"node_id": "BR-001"}, config=config)
# All 4 nodes execute: A, B, C, D
# You refactor node C's output model (add a field, rename a class, etc.)
# Run 2: only C and D re-execute
graph_v2 = compile(pipeline_v2, checkpointer=checkpointer)
run(graph_v2, input={"node_id": "BR-001"}, config=config)
# A and B preserved from checkpoint — C and D re-run
ChangeDetected?Behavior
Field added to output modelYesRewind to changed node
Field removedYesRewind
Field type changedYesRewind
Class renamed (same fields)YesRewind
Wrapper type swap (T -> ValidatedT)YesRewind
Node added/removed from pipelineYesFull re-run (topology change)
Prompt text changed (same types)NoNormal resume
Logic changed (same output type)NoNormal resume
  1. At compile time: neograph computes a SHA-256 fingerprint of each node’s output type and stores it on the compiled graph.
  2. At run time: the fingerprints are written to the checkpoint alongside node outputs.
  3. On resume: neograph compares stored fingerprints against current ones. If any differ, it uses LangGraph’s time-travel API to rewind to the checkpoint before the earliest changed node.
  4. Re-execution: invoke(None) from the rewind point naturally re-executes the changed node and everything downstream.

If you want explicit control instead of automatic rewind, use auto_resume=False:

from neograph import CheckpointSchemaError
try:
run(graph_v2, input={"node_id": "BR-001"}, config=config, auto_resume=False)
except CheckpointSchemaError as e:
print(f"Changed nodes: {e.invalidated_nodes}")
# e.invalidated_nodes = {"write_overview", "validate_completeness"}
# You decide: delete checkpoint, migrate, or handle manually

CheckpointSchemaError.invalidated_nodes tells you exactly which nodes changed, so you can make an informed decision.

Checkpoints created before fingerprinting was added (pre-0.5) don’t have stored fingerprints. Neograph handles this gracefully: it treats the checkpoint as valid and resumes normally. No false errors.

  • Use persistent checkpointers for expensive pipelines. SqliteSaver for local dev, PostgresSaver for production.
  • Keep auto_resume=True (the default). It saves you from manually deleting checkpoints every time you refactor a node.
  • Use auto_resume=False in CI/production if you want explicit control over schema migrations.
  • Don’t worry about prompt changes. Schema fingerprints are based on output types, not prompt text. You can iterate on prompts freely without invalidating checkpoints.

Built on LangGraph’s time-travel API (get_state_history + update_state). No custom checkpoint store manipulation.

Documentation: CC BY-ND 4.0, © Constantine Mirin, mirin.pro