AsyncSqliteSaver hangs when using astream_events
TL;DR: AsyncSqliteSaver holds its database connection open, and astream_events waits on it forever. Use the saver as an async context manager and the hang goes away. Nothing is wrong with your graph.
# no error, no traceback. the symptom: this loop never prints anything and never exits:
async for event in graph.astream_events(10, config, version="v1"):
print(event)
When this applies
- You compiled with
checkpointer=AsyncSqliteSaver... (not MemorySaver, not Postgres). invoke() or stream() works, but astream_events() hangs.- Killing the process is the only way out.
When it does not
- If
astream_events hangs with MemorySaver, the graph itself is stuck (check for an infinite loop, or a node awaiting something that never resolves). - If it hangs on Postgres, that is a connection-pool issue, not this.
Fix it
1. Use the saver as an async context manager
from langgraph.checkpoint.aiosqlite import AsyncSqliteSaver
async with AsyncSqliteSaver.from_conn_string(":memory:") as saver:
graph = builder.compile(checkpointer=saver)
config = {"configurable": {"thread_id": "thread-1"}}
async for event in graph.astream_events(10, config, version="v1"):
print(event)
# connection closes here, at the end of the block
Expected: events stream normally and the block exits. The context manager closes the connection, which is what the event loop was waiting on.
2. For a file-backed database, same pattern
async with AsyncSqliteSaver.from_conn_string("checkpoints.db") as saver:
graph = builder.compile(checkpointer=saver)
...
Expected: checkpoints persist to checkpoints.db and the process exits cleanly.
3. In a long-lived app, do not compile per request
If you need one graph for the whole app lifetime (a web server, a bot), create the saver once at startup inside a lifespan handler and close it at shutdown, instead of compiling inside each request handler.
Why it happens
AsyncSqliteSaver opens a real sqlite connection in async mode. astream_events drives the graph through async generators, and the open connection keeps the event loop's cleanup from completing, so the stream never terminates. The sync SqliteSaver and the in-memory saver do not hold the loop this way, which is why only the async sqlite saver hangs.
Edge cases
:memory: databases vanish when the context manager exits. If you need persistence across restarts, use a file path.- Mixing sync and async savers on the same file can corrupt it. Pick one.
- If you must manage the connection yourself, call the saver's close/aclose explicitly when you are done streaming. The context manager is just the tidy way.
Compatibility
langgraph 0.0.x through 1.x (Python), langgraph-checkpoint-sqlite package. The async with pattern is the documented usage.