Incident brief
Connection to client lost
A client was abruptly killed mid-query, severing the connection while the server still had output to send. PostgreSQL logged SQLSTATE 08006 and cleaned up that backend.
What lands in your log
FATAL: connection to client lost
In 10 seconds
- What triggers it
- Start a query from a client, then kill the client process outright (SIGKILL) before it finishes.
- Fix
- There's nothing to fix server-side for a single abrupt kill, this is PostgreSQL correctly detecting and cleaning up after a dead connection.
- Proof
- Reproduced on PostgreSQL 16.14 → Killing a client process outright (SIGKILL) mid-query produced a real broken-pipe/lost-connection entry in the server log tagged SQLSTATE 08006. An ordinary clean disconnect produced no such entry.
Fix
What to do right now
Application-level steps for this error.
- There's nothing to fix server-side for a single abrupt kill, this is PostgreSQL correctly detecting and cleaning up after a dead connection.
- If this happens routinely in production, look at what's killing the client (OOM killer, container restarts, load balancer timeouts) rather than at PostgreSQL.
-- No corrective SQL on the server for a single abrupt client kill.
-- Investigate the client path instead (examples):
-- * load balancer / proxy idle timeouts shorter than long queries
-- * container OOM kills (exit 137) on the app side
-- * network path drops mid-result
-- Confirm in the server log: SQLSTATE 08006 "connection to client lost"For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
The server log is authoritative for 08006 because the client may already be gone. Correlate the log timestamp with client identity and logging settings.
Live clients and connection age
Look for churn from one application/address around the failure window.
SELECT application_name, client_addr, state,
count(*) AS sessions,
min(backend_start) AS oldest_connection,
max(backend_start) AS newest_connection
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY application_name, client_addr, state
ORDER BY sessions DESC;Connection/disconnection log settings
Confirm whether the server is recording enough metadata to correlate disconnects.
SELECT name, setting
FROM pg_settings
WHERE name IN ('log_connections','log_disconnections','log_line_prefix','log_min_error_statement')
ORDER BY name;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual, not paraphrased.
PostgreSQL 16 Documentation, Appendix A. PostgreSQL Error Codes (Class 08, Connection Exception)
Class 08, Connection Exception ... 08006 connection_failureRead the full section on postgresql.org →
Client killed mid-query (SIGKILL)
The client process was killed with SIGKILL while the server was about to send query output back to it. The client's own shell shows 'Killed' and exit status 137, real, observable proof the process was terminated. The SQLSTATE 08006 tag only ever appears in the server log, since the client is already gone by the time the server notices.Ordinary clean disconnect
A completed query followed by an ordinary \q disconnect produced no error and no new server log entry at all, proving 08006 is specific to an abrupt, mid-write disconnection, not disconnecting itself.Reproduce & verify
A real reproduction using an abrupt client kill
A literal transcript of SQL run against a live PostgreSQL instance in an isolated lab. The commands below are exactly what was executed.
- 1Start a query from a client, then kill the client process outright (SIGKILL) before it finishes.
- 2The server tries to write to the now-gone socket and gets a broken pipe.
- 3The server log records SQLSTATE 08006: connection to client lost. The client itself never sees this message, its process is already dead.
One client process, killed mid-query with SIGKILL while the server still had output queued to send back to it.
-- No schema needed: this reproduces at the connection/transport level.
SELECT 'no schema required' AS setup_note;timeout -s KILL 2 psql -U postgres -d thesev1database -c "BEGIN; SELECT pg_sleep(10);"psql -U postgres -d thesev1database -c "SELECT 1;" -c "\q"What PostgreSQL actually returned
setup_note
--------------------
no schema required
(1 row)Killed
psql exit status: 137
-- server log, captured with log_error_verbosity=verbose (reverted right after):
LOG: could not send data to client: Broken pipe
FATAL: connection to client lost clean_disconnect_test
-----------------------
1
(1 row)
-- no new server log entry at allThe deeper lab audit for this error
- What does NOT trigger this error: exact SQL, output, and verdict
- Raw PostgreSQL server-log evidence
- A manual-grounded production interpretation of the lab result
Card required. Cancel before day 7 and you are not charged.
Runbook to fix this
Runbooks for this incident
Full step-by-step fixes for the condition behind this error: the diagnosis, the exact SQL, and output captured in the lab.
- Diagnose PgBouncer queueing and session-state leaksClients wait in PgBouncer even though PostgreSQL has capacity, or session settings behave inconsistently.Free
- Measure and fence a Patroni failoverYou have never timed failover, proved the old primary is fenced, or verified client routing.Free
- Prove point-in-time recovery from archived WALBackups exist, but nobody has proved a timestamp restore excludes later commits.Free
Connected
Everything this error touches
Every page this SQLSTATE connects to: the concept that explains it, the runbooks that fix it, the parameters you tune to prevent it, and the sibling errors it travels with. All real cross-references. Jump straight in, or open the full interactive map.
Understand the concept
Fix it — runbooks
Diagnose PgBouncer queueing and session-state leaksSurvive a connection storm when there is no poolerMeasure and fence a Patroni failoverMeasure what a failover costs your applicationRead replication lag as three numbersRejoin a demoted primary with pg_rewindRight-size synchronous_commit for latencyRoute writes through a failover without a proxyWhat happens when your synchronous standby diesMap RPO and RTO targets to a PostgreSQL HA topologyProve a monthly restore drill for auditorsProve point-in-time recovery from archived WALRelated errors
Verification
- Last verified
- 2026-07-16 (Docker lab, PostgreSQL 16.14)
- Verification scope
- Verified against PostgreSQL 16.14 in an isolated lab environment
- Audit status
- reviewed
Went further?
Pro unlocks the second lab proof
Free page stops the bleeding. Pro adds the operational test, SQLSTATE audit, and deeper evidence, same error, more certainty.