How LangGraph Checkpoint Storage Uses SQLite for State Persistence in Chat Sessions

LangGraph's SqliteSaver persists complete conversation states—including message history—to a local SQLite file, enabling seamless session resumption across API calls.

Open Notebook leverages LangGraph's checkpoint system to maintain conversation continuity in its chat interface. By configuring SQLite-based persistence, the application stores full thread states directly in a local database file, allowing the system to resume conversations exactly where they left off. The implementation centers on the SqliteSaver class from LangGraph's checkpoint module, which automatically serializes the graph state to JSON and retrieves it on demand using a unique thread identifier.

Configuring the SQLite Checkpoint File Location

The checkpoint database path is centralized in open_notebook/config.py to ensure consistency across the application.

DATA_FOLDER = "./data"
sqlite_folder = f"{DATA_FOLDER}/sqlite-db"
LANGGRAPH_CHECKPOINT_FILE = f"{sqlite_folder}/checkpoints.sqlite"

This configuration establishes the SQLite file at ./data/sqlite-db/checkpoints.sqlite. The database is created automatically on first use when the application initializes the first graph connection.

Initializing the SqliteSaver Connection

Both the main chat and source-chat graphs initialize their checkpoint savers in open_notebook/graphs/chat.py and open_notebook/graphs/source_chat.py. The implementation creates a standard sqlite3.Connection and wraps it with LangGraph's SqliteSaver.

import sqlite3
from langgraph.checkpoint.sqlite import SqliteSaver
from open_notebook.config import LANGGRAPH_CHECKPOINT_FILE

conn = sqlite3.connect(LANGGRAPH_CHECKPOINT_FILE, check_same_thread=False)
memory = SqliteSaver(conn)  # Checkpoint persistence object

Note: The check_same_thread=False parameter is required because SqliteSaver manages its own thread safety internally, while the connection may be accessed across different async contexts.

Compiling Graphs with Checkpoint Persistence

After initializing the saver, the graph compiler binds it to the state machine using the checkpointer parameter. This occurs in the graph definition files where ThreadState defines the state schema containing the messages field.

from langgraph.graph import StateGraph, END, START

agent_state = StateGraph(ThreadState)

# ... node and edge definitions ...

graph = agent_state.compile(checkpointer=memory)

When checkpointer=memory is passed to compile(), LangGraph automatically triggers a checkpoint write after every node execution. The ThreadState dictionary—including the messages list containing LangChain message objects—is serialized to JSON and stored under the configured thread ID.

Retrieving Persisted Session State

Since SqliteSaver operates synchronously, the async API routes in open_notebook/utils/graph_utils.py use asyncio.to_thread to fetch state without blocking the event loop.

import asyncio
from langchain_core.runnables import RunnableConfig

thread_state = await asyncio.to_thread(
    graph.get_state,
    config=RunnableConfig(configurable={"thread_id": session_id}),
)
message_count = len(thread_state.values["messages"])

The RunnableConfig passes the thread_id through the configurable dictionary, which SqliteSaver uses as the primary key to look up the serialized state in the SQLite database.

Understanding the Storage Schema

LangGraph manages the SQLite schema internally. Each row represents a single thread state keyed by the unique thread identifier.

What gets stored:

  • The complete ThreadState dictionary serialized as JSON
  • The messages array containing the full conversation history
  • Metadata including the current node and execution checkpoints

On every graph transition, the saver updates the existing row for that thread ID, ensuring the database always reflects the latest conversation transcript. This append-only style with versioned checkpoints allows for state recovery and replay if needed.

Summary

  • Configuration: The checkpoint file path is defined in open_notebook/config.py as ./data/sqlite-db/checkpoints.sqlite
  • Initialization: SqliteSaver wraps a sqlite3.Connection with check_same_thread=False to handle cross-thread access safely
  • Compilation: Graphs compiled with checkpointer=memory automatically persist state after each node execution
  • Retrieval: Use graph.get_state with a RunnableConfig containing thread_id to fetch persisted conversations
  • Data: The SQLite database stores JSON-serialized ThreadState objects, including the complete messages history

Frequently Asked Questions

Where is the SQLite checkpoint file stored?

According to open_notebook/config.py, the checkpoint file is located at ./data/sqlite-db/checkpoints.sqlite relative to the application root. This path is constructed by joining the DATA_FOLDER variable with the sqlite-db subdirectory, ensuring all persistent data resides in a single location for easy backup and migration.

How does LangGraph handle concurrent access to the SQLite database?

The implementation uses check_same_thread=False when creating the sqlite3.Connection, which allows the connection to be shared across threads. However, SqliteSaver manages synchronization internally, ensuring that state reads and writes are atomic. For production deployments with high concurrency, LangGraph recommends using a connection pool or switching to the PostgreSQL-based PostgresSaver instead.

Can I query the checkpoint database directly outside of LangGraph?

Yes, the SQLite file is a standard SQLite database that you can query using any SQLite client. The table storing checkpoints contains JSON blobs of the serialized state. However, direct manipulation is not recommended—use graph.get_state() and graph.update_state() through the LangGraph API to maintain data integrity and handle versioning correctly.

What happens to conversation history if the SQLite file is deleted?

If the checkpoint file is deleted or becomes corrupted, all conversation history is permanently lost. The application will recreate the file on the next initialization, but previous thread states will be unavailable. For production environments, implement regular backups of the ./data/sqlite-db/ directory or migrate to a managed PostgreSQL backend for durability.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →