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

> Learn how LangGraph checkpoint storage uses SQLite to save and resume chat session states. Persist conversation history and message data locally for seamless continuity.

- Repository: [Luis Novo/open-notebook](https://github.com/lfnovo/open-notebook)
- Tags: internals
- Published: 2026-06-28

---

**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`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/config.py) to ensure consistency across the application.

```python
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`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/chat.py) and [`open_notebook/graphs/source_chat.py`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/graphs/source_chat.py). The implementation creates a standard `sqlite3.Connection` and wraps it with LangGraph's `SqliteSaver`.

```python
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.

```python
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`](https://github.com/lfnovo/open-notebook/blob/main/open_notebook/utils/graph_utils.py) use `asyncio.to_thread` to fetch state without blocking the event loop.

```python
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`](https://github.com/lfnovo/open-notebook/blob/main/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`](https://github.com/lfnovo/open-notebook/blob/main/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.