Install any skill in seconds. Free to start, no credit card required.
Get Started Free →LangGraph checkpointing and persistence. Use when implementing fault-tolerant workflows, resuming interrupted executions, debugging with state history, or avoiding re-running expensive operations.
.claude/skills/majiayu000-langgraph-checkpoints/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-05 | ✗→✓ | ▲ Improved | 70% | 0% |
| case-08 | ✗→✓ | ▲ Improved | 27% | 0% |
| case-13 | ✗→✓ | ▲ Improved | 207% | 0% |
| case-20 | ✗→✓ | ▲ Improved | -16% | 0% |
| case-01 | ✓→✓ | = Same ✓ | 79% | 0% |
Persist workflow state for recovery and debugging.
pythonfrom langgraph.checkpoint import MemorySaver from langgraph.checkpoint.sqlite import SqliteSaver from langgraph.checkpoint.postgres import PostgresSaver # Development: In-memory memory = MemorySaver() app = workflow.compile(checkpointer=memory) # Production: SQLite checkpointer = SqliteSaver.from_conn_string("checkpoints.db") app = workflow.compile(checkpointer=checkpointer) # Production: PostgreSQL checkpointer = PostgresSaver.from_conn_string("postgresql://...") app = workflow.compile(checkpointer=checkpointer)
python# Start new workflow config = {"configurable": {"thread_id": "analysis-123"}} result = app.invoke(initial_state, config=config) # Resume interrupted workflow config = {"configurable": {"thread_id": "analysis-123"}} result = app.invoke(None, config=config) # Resumes from checkpoint
pythondef create_checkpointer(): """Create PostgreSQL checkpointer for production.""" return PostgresSaver.from_conn_string( settings.DATABASE_URL, save_every=1 # Save after each node ) # Compile with checkpointing app = workflow.compile( checkpointer=create_checkpointer(), interrupt_before=["quality_gate"] # Manual review point )
python# Get all checkpoints for a workflow checkpoints = app.get_state_history(config) for checkpoint in checkpoints: print(f"Step: {checkpoint.metadata['step']}") print(f"Node: {checkpoint.metadata['source']}") print(f"State: {checkpoint.values}") # Get current state current = app.get_state(config) print(current.values)
pythonimport logging async def run_with_recovery(workflow_id: str, initial_state: dict): """Run workflow with automatic recovery.""" config = {"configurable": {"thread_id": workflow_id}} try: # Try to resume existing workflow state = app.get_state(config) if state.values: logging.info(f"Resuming workflow {workflow_id}") return app.invoke(None, config=config) except Exception: pass # No existing checkpoint # Start fresh logging.info(f"Starting new workflow {workflow_id}") return app.invoke(initial_state, config=config)
python# Execute one node at a time for step in app.stream(initial_state, config): print(f"After {step['node']}: {step['state']}") input("Press Enter to continue...") # Rollback to previous checkpoint history = list(app.get_state_history(config)) previous_state = history[1] # One step back app.update_state(config, previous_state.values)
pythonfrom langgraph.checkpoint.postgres import PostgresSaver from langgraph.store.postgres import PostgresStore # Checkpointer = SHORT-TERM memory (thread-scoped) # - Conversation history within a session # - Workflow state for resume/recovery # - Scoped to thread_id checkpointer = PostgresSaver.from_conn_string(DATABASE_URL) # Store = LONG-TERM memory (cross-thread) # - User preferences across sessions # - Learned facts about users # - Shared across ALL threads for a user store = PostgresStore.from_conn_string(DATABASE_URL) # Compile with BOTH for full memory support app = workflow.compile( checkpointer=checkpointer, # Thread-scoped state store=store # Cross-thread memory )
pythonfrom langgraph.store.base import BaseStore async def agent_with_memory(state: AgentState, *, store: BaseStore): """Agent that remembers across conversations.""" user_id = state["user_id"] # Read cross-thread memory (user preferences) memories = await store.aget(namespace=("users", user_id), key="preferences") # Use memories in agent logic if memories and memories.value.get("prefers_concise"): state["system_prompt"] += "\nBe concise in responses." # Update cross-thread memory (learned facts) await store.aput( namespace=("users", user_id), key="last_topic", value={"topic": state["current_topic"], "timestamp": datetime.now().isoformat()} ) return state # Register node with store access workflow.add_node("agent", agent_with_memory)
┌─────────────────────────────────────────────────────────────┐
│ User: alice │
├─────────────────────────────────────────────────────────────┤
│ Thread 1 (chat-001) │ Thread 2 (chat-002) │
│ ┌─────────────────┐ │ ┌─────────────────┐ │
│ │ Checkpointer │ │ │ Checkpointer │ │
│ │ - msg history │ │ │ - msg history │ │
│ │ - workflow pos │ │ │ - workflow pos │ │
│ └─────────────────┘ │ └─────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ Store (cross-thread) │
│ namespace=("users", "alice") │
│ - preferences: {prefers_concise: true} │
│ - last_topic: {topic: "langgraph", timestamp: "..."} │
└─────────────────────────────────────────────────────────────┘| Decision | Recommendation | |----------|----------------| | Development | MemorySaver (fast, no setup) | | Production | PostgresSaver (shared, durable) | | save_every | 1 for expensive nodes, 5 for cheap | | Thread ID | Use deterministic ID (workflow_id) | | Short-term memory | Checkpointer (thread-scoped) | | Long-term memory | Store (cross-thread, namespaced) |
langgraph-state - State design for checkpointinglanggraph-human-in-loop - Interrupt patternsdatabase-schema-designer - PostgreSQL setupKeywords: save checkpoint, checkpoint, persist state, save state Solves:
Keywords: load checkpoint, restore, resume, recovery Solves:
Keywords: memory backend, MemorySaver, SqliteSaver, PostgresSaver Solves:
Keywords: async checkpoint, AsyncSqliteSaver, async persistence Solves:
Keywords: conversation, history, message history, thread Solves:
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | pass→pass | 16,161 | 17,397 | +8% | 1 | 1 | 0% | 2,368 | 4,244 | +79% | 0 | 0 | — |
case-02 | pass→pass | 7,788 | 5,145 | -34% | 1 | 1 | 0% | 1,156 | 2,714 | +135% | 0 | 0 | — |
case-03 | pass→pass | 12,222 | 10,020 | -18% | 1 | 1 | 0% | 2,004 | 2,695 | +34% | 0 | 0 | — |
case-04 | pass→pass | 12,928 | 9,909 | -23% | 1 | 1 | 0% | 2,552 | 3,699 | +45% | 0 | 0 | — |
case-05 | fail→pass | 18,113 | 14,976 | -17% | 1 | 1 | 0% | 2,561 | 4,357 | +70% | 0 | 0 | — |
case-06 | pass→pass | 8,401 | 5,566 | -34% | 1 | 1 | 0% | 1,574 | 2,755 | +75% | 0 | 0 | — |
case-07 | pass→pass | 12,417 | 7,392 | -40% | 1 | 1 | 0% | 2,341 | 3,232 | +38% | 0 | 0 | — |
case-08 | fail→pass | 18,259 | 16,140 | -12% | 1 | 1 | 0% | 3,339 | 4,250 | +27% | 0 | 0 | — |
case-09 | pass→pass | 6,574 | 3,125 | -52% | 1 | 1 | 0% | 897 | 2,342 | +161% | 0 | 0 | — |
case-10 | pass→pass | 5,228 | 3,445 | -34% | 1 | 1 | 0% | 911 | 2,484 | +173% | 0 | 0 | — |
case-11 | pass→pass | 11,523 | 4,312 | -63% | 1 | 1 | 0% | 2,110 | 2,654 | +26% | 0 | 0 | — |
case-12 | pass→pass | 7,425 | 7,269 | -2% | 1 | 1 | 0% | 1,336 | 3,246 | +143% | 0 | 0 | — |
case-13 | fail→pass | 4,670 | 3,599 | -23% | 1 | 1 | 0% | 793 | 2,438 | +207% | 0 | 0 | — |
case-14 | pass→pass | 3,352 | 2,983 | -11% | 1 | 1 | 0% | 602 | 2,358 | +292% | 0 | 0 | — |
case-15 | pass→pass | 8,003 | 6,709 | -16% | 1 | 1 | 0% | 1,345 | 2,946 | +119% | 0 | 0 | — |
case-16 | pass→pass | 5,322 | 3,022 | -43% | 1 | 1 | 0% | 982 | 2,464 | +151% | 0 | 0 | — |
case-17 | pass→pass | 3,352 | 2,820 | -16% | 1 | 1 | 0% | 595 | 2,292 | +285% | 0 | 0 | — |
case-18 | pass→pass | 7,329 | 3,083 | -58% | 1 | 1 | 0% | 1,423 | 2,375 | +67% | 0 | 0 | — |
case-19 | pass→pass | 7,754 | 7,047 | -9% | 1 | 1 | 0% | 1,411 | 3,168 | +125% | 0 | 0 | — |
case-20 | fail→pass | 17,696 | 5,332 | -70% | 1 | 1 | 0% | 3,059 | 2,567 | -16% | 0 | 0 | — |
case-21 | pass→pass | 14,880 | 14,293 | -4% | 1 | 1 | 0% | 2,514 | 3,877 | +54% | 0 | 0 | — |
case-22 | pass→pass | 16,466 | 14,294 | -13% | 1 | 1 | 0% | 2,356 | 4,418 | +88% | 0 | 0 | — |
case-23 | pass→pass | 6,628 | 6,105 | -8% | 1 | 1 | 0% | 1,218 | 2,909 | +139% | 0 | 0 | — |
case-24 | pass→pass | 5,312 | 2,719 | -49% | 1 | 1 | 0% | 970 | 2,295 | +137% | 0 | 0 | — |
case-25 | pass→pass | 8,330 | 4,771 | -43% | 1 | 1 | 0% | 1,229 | 2,711 | +121% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 25 cases were attempted. The headline lift of +16 percentage points is the difference between those two pass rates over the 25 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.