POSTGRESQL ERROR 42P01: RELATION DOES NOT EXIST ------------------------------------------------------------------------ The table usually exists. PostgreSQL looked for it along search_path and did not find it there, or the name was folded to lower case and no longer matches a table created with capitals. Both cases report the same error as a genuinely missing table. CONFIDENCE : high VERIFIED : 2026-08-08 (fresh, 0d old) URL : https://knowbase.sh/k/postgres-42p01-relation-does-not-exist ERROR ------------------------------------------------------------------------ ERROR: relation "x" does not exist CODES: 42P01, undefined_table PROBLEM ------------------------------------------------------------------------ A query fails saying the relation does not exist, but the table is visibly there in psql or a GUI client. The same SQL works for one user and fails for another, or works in psql and fails from the application, which makes it look like a permissions or connection problem rather than a name resolution one. ROOT CAUSE ------------------------------------------------------------------------ 1. [primary] The table is in a schema that is not on search_path Unqualified names are resolved by walking search_path and taking the first match. A table in a schema outside that list is invisible even though it exists in the same database — and the error is identical to it not existing at all. how to tell: SHOW search_path does not list the schema, while the table appears in information_schema.tables under a different schema name 2. [primary] The identifier was created quoted with capitals Unquoted names are folded to lower case. A table created as "MyTable" can only be referenced as "MyTable" with quotes; writing MyTable becomes mytable and does not match. ORMs that quote on create but not on query cause exactly this. how to tell: The table name in information_schema.tables contains capitals, and the query succeeds when the name is double-quoted 3. [common] The connection is to the wrong database search_path is per-session and schemas are per-database. A connection string pointing at the default database rather than the application's will report every table missing. how to tell: SELECT current_database(), current_schema() returns something other than what the application expects 4. [common] The migration has not run on this environment The table exists in development and not in the target. Straightforward, but worth excluding before chasing search_path — it is the only cause where the table is genuinely absent. how to tell: The table is absent from information_schema.tables in every schema, not merely outside search_path 5. [edge] The object exists but is not the kind being used Referencing a sequence, view or type where a table is expected, or querying a composite type by name. The name resolves to something, just not to a relation usable in that position. how to tell: pg_class lists the name with a relkind other than r or p, or the object is a type rather than a relation 6. [edge] A temporary table from another session Temporary tables live in a per-session schema and vanish with the connection. With a pooled connection the table may have been created on a different physical session than the one now querying. how to tell: The table was created as TEMPORARY and the application uses a connection pool SOLUTION ------------------------------------------------------------------------ 1. Ask the database where the table actually is, across every schema. This single query separates the four common causes from each other. $ psql -c "SELECT table_schema, table_name FROM information_schema.tables WHERE table_name ILIKE '%your_table%';" note: ILIKE is deliberate — a case-insensitive match reveals a name created with capitals, which an exact match would hide. 2. Check what the failing session can see, since search_path is per-session and may differ from psql's. $ psql -c 'SELECT current_database(), current_schema(), current_user;' -c 'SHOW search_path;' 3. If it is a schema problem, prefer qualifying the name over changing the path. A qualified reference resolves the same way for every user and role. -- ✅ unambiguous regardless of search_path SELECT * FROM analytics.events; -- session-scoped alternative SET search_path TO analytics, public; -- persistent, per role ALTER ROLE app_user SET search_path TO analytics, public; 4. If it is a case problem, decide on one convention and hold it. Lower case unquoted is the path of least resistance in PostgreSQL; the alternative is quoting the name everywhere, forever. -- created quoted, so it must always be quoted CREATE TABLE "MyTable" (id int); SELECT * FROM MyTable; -- 🔴 folded to mytable, fails SELECT * FROM "MyTable"; -- ✅ -- rename to the convention instead of quoting forever ALTER TABLE "MyTable" RENAME TO my_table; 5. Verify the migration actually ran against this database before assuming name resolution is at fault. $ psql -c "SELECT * FROM schema_migrations ORDER BY version DESC LIMIT 5;" 6. For pooled connections, never rely on a temporary table surviving between statements — create it and use it inside one transaction, or use an unlogged table with an explicit lifetime. VERIFY: The failing query runs from the application's own connection, and information_schema.tables confirms the schema and exact spelling the query uses. FALLBACK: Where an ORM generates unqualified names you cannot control, set search_path on the role rather than in application code, so every connection that role opens resolves names the same way regardless of where the query is built. APPLIES TO ------------------------------------------------------------------------ PostgreSQL: 9.0 and later (Unquoted identifiers fold to lower case, which is incompatible with the SQL standard's upper-case folding — portable code should quote consistently or never.) search_path: all versions (Defaults to "$user", public — so a custom schema is invisible until added.) platforms: self-hosted, managed PostgreSQL NOT APPLICABLE TO ------------------------------------------------------------------------ - Permission errors (42501), where the relation is found but access is denied - SQLSTATE 42703 undefined_column, which resolves the table but not a column in it - MySQL error 1146 table doesn't exist, which has no search_path equivalent - Connection failures, which never reach name resolution at all EVIDENCE ------------------------------------------------------------------------ 1. PostgreSQL — The Schema Search Path https://www.postgresql.org/docs/current/ddl-schemas.html The PostgreSQL Global Development Group | official-docs | read 2026-08-08 supports: That unqualified names resolve along search_path, that the first match wins, and that no match produces an error even when the table exists in another schema of the same database — the exact behaviour behind the primary cause. 2. PostgreSQL — Identifiers and Key Words https://www.postgresql.org/docs/current/sql-syntax-lexical.html The PostgreSQL Global Development Group | official-docs | read 2026-08-08 supports: That quoting makes an identifier case-sensitive while unquoted names fold to lower case, so a table created as "MyTable" cannot be reached by writing MyTable. 3. PostgreSQL Error Codes (Appendix A) https://www.postgresql.org/docs/current/errcodes-appendix.html The PostgreSQL Global Development Group | official-docs | read 2026-08-08 supports: That 42P01 is undefined_table in class 42, Syntax Error or Access Rule Violation — a name-resolution class rather than a missing-object one. CONFIDENCE ------------------------------------------------------------------------ high — The search_path resolution rule and the case-folding rule are quoted verbatim from PostgreSQL's own reference, and they account for the two primary causes. The pooled temporary-table case follows from documented temporary-schema behaviour rather than from a sentence about this error, and the diagnostic queries are standard practice rather than documented procedure. ------------------------------------------------------------------------ knowbase 0.1.0 — CC-BY-4.0