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.
In 10 seconds
- What
- Connection to client lost
- What triggers it
- Start a query from a client, then kill the client process outright (SIGKILL) before it finishes.
- The 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.
The fix
What to do right now
The immediate, application-level response to 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.
-- There is no "fix" SQL here: the server's behavior (detect the broken pipe,
-- log 08006, and clean up the backend) is already correct.
SELECT 1 AS clean_disconnect_example;Diagnose
See it live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Standard triage — not specific to this error
These are canonical PostgreSQL system-catalog queries, shown as SQL to run. No sample output is attached because this is general triage, not a captured lab transcript.This SQLSTATE does not have an error-specific live snapshot yet. These are the canonical system-catalog queries you run against the affected server to see the problem in real time — standard triage, not a reproduced transcript.
What is running right now
Active backends, how long each has been running, and what it is waiting on.
SELECT pid,
state,
wait_event_type,
wait_event,
now() - query_start AS running_for,
left(query, 80) AS query
FROM pg_stat_activity
WHERE state <> 'idle'
AND pid <> pg_backend_pid()
ORDER BY running_for DESC NULLS LAST;Who is blocking whom
Turn raw blocking PIDs into the actual queries on both sides of the wait.
SELECT blocked.pid AS blocked_pid,
blocked.query AS blocked_query,
blocking.pid AS blocking_pid,
blocking.query AS blocking_query
FROM pg_stat_activity AS blocked
JOIN LATERAL unnest(pg_blocking_pids(blocked.pid)) AS b(pid) ON true
JOIN pg_stat_activity AS blocking ON blocking.pid = b.pid
WHERE cardinality(pg_blocking_pids(blocked.pid)) > 0;Locks that are still waiting
Every lock a backend has requested but not yet been granted.
SELECT l.pid,
l.locktype,
l.mode,
l.granted,
COALESCE(c.relname, l.transactionid::text) AS object
FROM pg_locks l
LEFT JOIN pg_class c ON c.oid = l.relation
WHERE NOT l.granted
ORDER BY l.pid;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 Pro unlocks here
- The exact prevention SQL — copy-paste ready
- Raw psql output captured from the Docker lab
- A senior-DBA action list to take it further
- Live monitoring queries to catch it in production
- The deeper audit: fix-that-fails counterexample, GUC before/after, server-log evidence
Related & next steps
Follow the thread
Everything this error touches — jump straight to the sibling error, term, runbook, or parameter.
Verification
- Last verified
- 2026-07-16 (Docker lab, PostgreSQL 16.14)
- Reviewed by
- Verified against PostgreSQL 16.14 in an isolated lab environment
- Audit status
- reviewed