psycopg2.OperationalError: server closed the connection unexpectedly
Python · updated 2026-11-02
Raised by psycopg (and wrapped by SQLAlchemy as sqlalchemy.exc.OperationalError) on the first query sent over a connection the server or a proxy already closed. It usually hits the first request after a quiet period, after a database restart or failover, or behind PgBouncer, a cloud proxy or a load balancer with an idle timeout.
sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) server closed the connection unexpectedly This probably means the server terminated abnormally before or while processing the request. [SQL: SELECT orders.id FROM orders WHERE orders.customer_id = %(customer_id_1)s]
Quick fix
Let the pool test connections before using them and replace stale ones. In SQLAlchemy set pool_pre_ping=True and pool_recycle below the shortest idle timeout between your app and the database. Retry the query once for read-only work when the error comes from a dropped connection.
- Note when it happens: first request after idle time, after a deploy or restart, or under load.
- Turn on pool_pre_ping=True so dead connections are replaced before a query uses them.
- Set pool_recycle (seconds) below the idle timeout of PgBouncer, the cloud proxy or the load balancer.
- Check the Postgres log for the same time: a restart, failover, OOM kill or idle_session_timeout explains it.
- For long jobs, open a fresh session per unit of work instead of holding one connection for hours.
from sqlalchemy import create_engine
engine = create_engine(
DATABASE_URL,
pool_pre_ping=True, # test each connection on checkout, reconnect if it is dead
pool_recycle=280, # seconds; below the shortest idle timeout on the path to Postgres
)Why it happens
Idle connections cut by a proxy or load balancer
PgBouncer, cloud SQL proxies and TCP load balancers close connections that sit idle past their timeout. The pool still thinks they are open.
The database restarted or failed over
Every pooled connection from before the restart is dead, and each one fails once on first use.
Server-side timeouts
idle_session_timeout or idle_in_transaction_session_timeout on the Postgres side end sessions the app still holds.
The backend was killed
A query that ran out of memory or was cancelled by an admin can take its connection down with it; check the Postgres log.
Other fixes
- Keep transactions short so connections are not held idle in the middle of one.
- Monitor pool checkouts and reconnects so a failover shows up as a spike, not a mystery.
- For psycopg 3 without SQLAlchemy, use psycopg_pool with a check callback (ConnectionPool.check_connection).
Same error, other wording
- psycopg.OperationalError: consuming input failed: server closed the connection unexpectedly
- sqlalchemy.exc.OperationalError: (psycopg2.OperationalError) SSL SYSCALL error: EOF detected
- psycopg2.InterfaceError: connection already closed
Let your coding agent catch the next one
Runheed records this error in production with the stack trace, the breadcrumbs before it and the agent session that shipped the code, and serves it to Claude Code, Cursor or any MCP client. With it connected, you can ask:
> Find the "server closed the connection unexpectedly" errors in production, line them up with deploys, restarts and idle periods, and set up the connection pool (pre-ping and recycle) so stale connections are replaced before use.
Related errors
- requests.exceptions.ConnectionError: HTTPSConnectionPool(host='api.example.com'...
- RuntimeError: Event loop is closed
- KeyError: 'email'
References: SQLAlchemy: Dealing with disconnects (pool_pre_ping), PostgreSQL: Client connection defaults (idle_session_timeout)