Incident brief
Relation does not exist
A query referenced a table that PostgreSQL couldn't find. Usually a typo — but the table can also just be in a schema that isn't being searched.
In 10 seconds
- What
- Relation does not exist
- What triggers it
- Query a table name that doesn't exist anywhere in the database.
- The fix
- Fix a typo'd table name if that's the cause.
- Proof
- Reproduced on PostgreSQL 16.14 → Querying a table name that doesn't exist anywhere in the database was rejected with SQLSTATE 42P01. The session was unaffected afterward.
The fix
What to do right now
The immediate, application-level response to this error.
- Fix a typo'd table name if that's the cause.
- If the table exists in a different schema, qualify it (schema.table) or add that schema to search_path.
- Double-check which schema a new object actually landed in — see the premium test below for how search_path decides this.
-- schema-qualify the table name directly, no search_path change needed
SELECT * FROM archive.daily_metrics;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 — §5.9.3 The Schema Search Path
If there is no match in the search path, an error is reported, even if matching table names exist in other schemas in the database.Read the full section on postgresql.org →
The query against a nonexistent table
PostgreSQL searched every schema on the current search_path, found no match, and reported the exact name it looked for.Confirming the session still works
Outside an explicit transaction, the failed query didn't affect anything else — the next statement ran normally.Reproduce & verify
A real, single-session PostgreSQL reproduction
A literal transcript of SQL run against a live PostgreSQL instance in an isolated lab — the commands below are exactly what was executed.
- 1Query a table name that doesn't exist anywhere in the database.
- 2PostgreSQL rejects the query with SQLSTATE 42P01, naming the exact relation it couldn't find.
- 3The session itself is unaffected — the next statement runs normally.
One client: query a table name that was never created.
-- No schema needed: this error only requires querying a table that doesn't exist.
SELECT 1;SELECT * FROM monthly_report;SELECT 1 AS session_still_alive;What PostgreSQL actually returned
?column?
----------
1
(1 row)ERROR: relation "monthly_report" does not exist
LINE 1: SELECT * FROM monthly_report;
^ session_still_alive
----------------------
1
(1 row)The search_path fix was tested directly: a table that genuinely exists but sits outside the current search_path errors when referenced unqualified, then succeeds once its schema is added to search_path.
Create a table only in archive (not on the default search_path). Query it unqualified first, then again after adding its schema to search_path.
Without this
Before: the table genuinely exists, but isn't on the current search_path — the unqualified name still fails with SQLSTATE 42P01.
With this, tested
After: adding archive to search_path lets the exact same unqualified name resolve successfully.
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.
Related errors
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