Your handler's shape decides whether an interrupted agent run stops, finishes, or half-completes in the shadows. Not your checkpointer.
I wired a four-node LangGraph agent to a real uvicorn server, closed the client connection six hundred milliseconds in - after charge ran but before notify started. The thread stored next=['notify']. Then a user came back on the same thread_id and the graph ran again from the start. charge fired twice. One duplicate sat visible in the transcript. Change one setting and that duplicate vanishes completely - which is worse.
No crash. No worker death. No region failover. Just someone closing a connection. But the side effect already executed with no durable record of it.
๐๐๐ง๐๐ฅ๐๐ซ-๐๐ก๐๐ฉ๐๐ ๐๐ฎ๐ซ๐๐๐ข๐ฅ๐ข๐ญ๐ฒ: ๐ญ๐ก๐ ๐๐ฅ๐๐ข๐ฆ
In a self-hosted FastAPI service, your request handler's structure determines whether an interrupted agent run stops, completes, or half-completes. Three handler shapes - all passing code review, all serving the same response - produce three different durable outcomes from one disconnect. It is not your checkpointer. It is not your durability setting. It is the handler.
๐๐ก๐ฒ ๐ญ๐ก๐ข๐ฌ ๐ฆ๐๐ญ๐ญ๐๐ซ๐ฌ ๐ข๐ง ๐ฉ๐ซ๐จ๐๐ฎ๐๐ญ๐ข๐จ๐ง
The disconnect path is not rare. Closed laptop. Mobile handoff. Proxy idle timeout at 60 seconds. A user who lost interest. In a consumer-facing agent it runs at volume and drives your system down the code path you built for crash recovery and have probably never tested. The "transport gap" is real: HTTP streaming binds the connection, the device, and the server to one request. A drop on either side ends the session with no way to resume - and no one flags what already committed on the way out.
๐๐ก๐๐ญ ๐ฒ๐จ๐ฎ ๐ง๐๐๐ ๐ญ๐จ ๐๐๐๐ข๐๐ ๐ง๐จ๐ฐ
Do you need your agent's side effects to be idempotent? Do you need to detect duplicates after the fact? Do you need to prevent them altogether? Your answer shapes whether you use a detached handler (fire-and-forget, no visibility), an attached handler (blocks the response, highest safety), or a hybrid with explicit concurrency control. None of these choices live in your checkpointer. They live in your handler.
Read the full analysis with measurements on real PostgreSQL checkpoints, the competing handler shapes side-by-side, and what each one actually commits:
https://ranjankumar.in/fastapi-langgraph-client-disconnect-durability
Follow for more practitioner-grounded AI systems writing.
Top comments (0)