PostgreSQL error reference
The errors PostgreSQL actually throws at you in production, one page each: what the message means, the typical causes, how to diagnose it and how to fix it — with real commands, not hand-waving.
Connections & authentication
too many clients
max_connections is exhausted. How to see who is holding the slots, and why the real fix is usually a connection pooler, not a bigger limit.
Connectionsconnection refused
The TCP connection never reached PostgreSQL at all. Server down, wrong port, listen_addresses, firewall or Docker networking — in that order.
SQLSTATE 28P01password authentication failed
Sometimes the password really is wrong. The other times: the role has no password, SCRAM/md5 mismatch, or a URI that needed percent-encoding.
SQLSTATE 28000no pg_hba.conf entry
The server is reachable but no pg_hba.conf rule matches your (host, database, user, SSL) combination. The error tells you exactly which.
SQLSTATE 3D000database does not exist
Often the database named in the error is your own username — psql's default when you don't specify one. Or you're on the wrong port entirely.
SQLSTATE 28000role does not exist
Fresh install, and PostgreSQL doesn't know who "ubuntu" is. Why the OS username shows up, and how to bootstrap roles properly.
SQLSTATE 08006server closed the connection
Either the server process died (the log will say) or a firewall/NAT silently cut an idle connection (the log will be silent). Check the log first.
Concurrency & locks
deadlock detected
Two transactions each hold a lock the other needs. Why it happens, how to read the DETAIL lines, and the lock-ordering habits that prevent it.
SQLSTATE 40001could not serialize access
Repeatable Read or Serializable refused to pretend two conflicting transactions both happened. Not a bug — but your code must retry.
SQLSTATE 25P02current transaction is aborted
Never the real problem: an earlier statement in the transaction already failed, and PostgreSQL is refusing everything until you ROLLBACK.
SQLSTATE 55P03lock timeout
The statement gave up waiting for a lock — because someone sensibly set lock_timeout. Find the blocker, and keep the fail-fast pattern.
SQLSTATE 40001conflict with recovery
Only on replicas: WAL replay needed to remove row versions your query was still using. The fix is a trade-off you must choose consciously.
Constraints & data integrity
duplicate key value
A unique constraint did its job. The interesting question is why: an insert race, a sequence left behind by an import, or a client retrying.
SQLSTATE 23502not-null violation
A NOT NULL column would end up empty. The sneaky variant: your ORM sends an explicit NULL, which overrides the column's DEFAULT.
SQLSTATE 23503foreign key violation
Two directions, one constraint: a child pointing at a missing parent, or a parent being deleted while children still reference it.
SQLSTATE 22P02invalid input syntax
A text value could not be parsed as the target type. Usually a cast on dirty data, an empty string where a number was expected, or a CSV header.
SQLSTATE 22001value too long
Data exceeded a varchar(n) limit and PostgreSQL refuses to truncate silently. Often the real fix is questioning the (n) itself.
SQLSTATE 22012division by zero
A denominator hit zero — usually an empty group in an aggregation. The one-line fix worth memorizing: NULLIF(denominator, 0).
SQLSTATE 42P10no constraint matching ON CONFLICT
ON CONFLICT needs a unique index on exactly the columns (or expression, or partial predicate) you named. A regular index does not count.
SQL, types & schema
permission denied
Your role lacks a privilege — often because tables were created by the migration role and nobody granted the app role anything on them.
SQLSTATE 42P01relation does not exist
The table exists — you're just not looking where PostgreSQL is looking. search_path, quoted identifiers and wrong-database, in that order.
SQLSTATE 42703column does not exist
Three traps cause most of these: camelCase columns needing quotes, double-quoted "strings" (those are identifiers!), and aliases in WHERE.
SQLSTATE 42601syntax error at or near
The caret points where the parser gave up — the mistake is usually at or just before it. Reserved words and MySQL syntax top the list.
SQLSTATE 42883function does not exist
Read it as: no function with that name and those argument types. The function usually exists — your arguments are the wrong type.
SQLSTATE 42803must appear in GROUP BY
You selected a column that is neither grouped nor aggregated. Often the real intent is "latest row per group" — which wants DISTINCT ON, not GROUP BY.
SQLSTATE 21000more than one row returned
A scalar subquery promised at most one row and delivered several. Either you meant IN — or your data has duplicates it should not have.
Resources & operations
statement timeout
A query blew past statement_timeout. Find out whether it was slow, blocked on a lock, or the timeout was set somewhere you forgot about.
SQLSTATE 53200out of shared memory
Despite the scary name, it's almost always the shared lock table filling up — typically one transaction touching thousands of partitions.
SQLSTATE 53100no space left on device
The disk is full — and on PostgreSQL hosts the culprit is often WAL retained by a dead replication slot. Rule one: never hand-delete from the data directory.
Operationswraparound protection
The emergency brake of MVCC: transaction IDs nearly exhausted because freezing fell behind. Find what blocked vacuum, then let it catch up.
Finding the SQLSTATE of any error
Every PostgreSQL error carries a five-character SQLSTATE code, which is stabler than the message text (and what your driver exposes for programmatic handling). In psql, run \set VERBOSITY verbose and the code is printed with each error; in application code, read the SQLSTATE field from your driver's error object. The pages above list the SQLSTATE each error corresponds to.
