langchain-ai / langchain-ai/langgraph
[Bug]: Potential sqlite3.OperationalError database is locked during highly concurrent aput operations
- Dominant language
- Python
- Stars
- 41.8k
- Forks
- 7.1k
- Avg merge
- 23h 7m
- Merged PRs (30d)
- 30
Description
### Checked other resources
- [x] This is a bug, not a usage question.
- [x] I added a clear and descriptive title that summarizes this issue.
- [x] I used the GitHub search to find a similar question and didn't find it.
- [x] I am sure that this is a bug in LangGraph rather than my code.
- [x] The bug is not resolved by updating to the latest stable version of LangGraph (or the specific integration package).
- [x] This is not related to the langchain-community package.
- [x] I posted a self-contained, minimal, reproducible example. A maintainer can copy it and run it AS IS.
### Related Issues / PRs
None
### Reproduction Steps / Example Code (Python)
```python
import asyncio
from langchain_core.runnables import RunnableConfig
from langgraph.checkpoint.base import Checkpoint, CheckpointMetadata
from langgraph.checkpoint.sqlite.aio import AsyncSqliteSaver
async def simulate_concurrent_writes():
# Initialize the checkpointer in memory
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
await saver.setup()
# Prepare mock checkpoint data
config: RunnableConfig = {"configurable": {"thread_id": "test_thread", "checkpoint_ns": ""}}
checkpoint: Checkpoint = {"v": 1, "id": "1", "ts": "2026-01-01T00:00:00Z", "channel_values": {}, "channel_versions": {}, "versions_seen": {}}
metadata: CheckpointMetadata = {"source": "input", "step": 1, "writes": {}}
new_versions = {}
# Worker simulating concurrent agent writes
async def worker(worker_id):
cfg = config.copy()
cfg["configurable"] = config["configurable"].copy()
cfg["configurable"]["checkpoint_id"] = f"check_{worker_id}"
await saver.aput(cfg, checkpoint, metadata, new_versions)
# Trigger 50 concurrent writes at the exact same time
# Without BEGIN IMMEDIATE, this highly concurrent scenario can expose
# the database to "sqlite3.OperationalError: database is locked"
tasks = [worker(i) for i in range(50)]
await asyncio.gather(*tasks)
if __name__ == "__main__":
asyncio.run(simulate_concurrent_writes())
```
### Error Message and Stack Trace (if applicable)
```shell
```
### Description
### Description
In high-concurrency environments, when multiple async agents attempt to write checkpoints simultaneously using `AsyncSqliteSaver.aput`, SQLite can occasionally fail with a `sqlite3.OperationalError: database is locked`.
### Proposed Fix
Enforcing a `BEGIN IMMEDIATE` transaction block within the `async with self.lock` section inside `langgraph/checkpoint/sqlite/aio.py` ensures that SQLite acquires a write lock before executing the statement, preventing concurrent write collisions.
### System Info
System Information
------------------
> OS: Windows
> OS Version: 10.0.26200
> Python Version: 3.11.9 (tags/v3.11.9:de54cf5, Apr 2 2024, 10:12:12) [MSC v.1938 64 bit (AMD64)]
Package Information
-------------------
> langchain_core: 1.4.7
> langsmith: 0.8.16
> langchain_protocol: 0.0.17
Optional packages not installed
-------------------------------
> deepagents
> deepagents-cli
Other Dependencies
------------------
> httpx: 0.28.1
> jsonpatch: 1.33
> orjson: 3.11.9
> packaging: 26.2
> pydantic: 2.13.4
> pyyaml: 6.0.3
> requests: 2.32.3
> requests-toolbelt: 1.0.0
> rich: 15.0.0
> tenacity: 9.1.4
> typing-extensions: 4.15.0
> uuid-utils: 0.16.2
> websockets: 15.0
> wrapt: 2.2.1
> xxhash: 3.7.0
> zstandard: 0.25.0
Contributor guide
Research direction
Start in langgraph/checkpoint/sqlite/aio.py, focusing on AsyncSqliteSaver.aput and the async lock section described in the issue. Run the provided 50-worker reproduction with AsyncSqliteSaver.from_conn_string(":memory:") and verify that concurrent writes complete without sqlite3.OperationalError: database is locked.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, sqlite
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100