| title | PostgreSQL Relation Does Not Exist | |||||
|---|---|---|---|---|---|---|
| slug | postgresql-relation-does-not-exist | |||||
| technologies |
|
|||||
| severity | medium | |||||
| tags |
|
|||||
| related |
|
|||||
| last_reviewed | 2026-06-27 |
ERROR: relation "orders" does not exist
LINE 1: SELECT * FROM orders WHERE status = 'open';
^
ERROR: relation "public.orders" does not exist
In PostgreSQL a relation is a table, view, materialized view, sequence, or
index. This ERROR is the planner reporting that an unqualified or qualified
name in a query does not resolve to any relation visible to the session. The
caret (^) points at the offending name. Crucially, "does not exist" often means
"not found on the current search_path" rather than "absent from the database" β
the object may exist in a schema the session is not looking in. It is one of the
most common errors after a missed migration or a schema/search_path mismatch.
- postgresql (query planner, schema/search_path resolution)
medium β the failing query (and the feature behind it) breaks, but the database and other queries are unaffected. High if it blocks a core code path on every request.
- The migration that creates the table has not run in this environment β the most common cause.
search_pathdoes not include the schema the object lives in (e.g. object is inappbutsearch_pathis"$user", public).- Schema-qualification mismatch: the object is
reporting.ordersbut the query says justorders. - Case sensitivity: the table was created as
"Orders"(quoted) but queried asorders, which folds to lower case. - Connected to the wrong database where the table does not exist.
When the planner sees an unqualified relation name, it walks the session's
search_path schema by schema and uses the first matching relation; if none
matches, it raises this error. A schema-qualified name is resolved directly and
fails only if that exact schema.relation pair is absent. Because object names are
folded to lower case unless double-quoted at creation time, CREATE TABLE "Orders" produces a relation that an unquoted SELECT ... FROM Orders cannot
find. The practical first question is always: does the relation exist somewhere,
and is its schema on my search_path?
# Where (which schema) does the relation actually live?
psql -d appdb -c "SELECT schemaname, tablename FROM pg_tables WHERE tablename = 'orders';"
# Broader: any relation by this name across schemas, with type
psql -d appdb -c "SELECT n.nspname AS schema, c.relname, c.relkind
FROM pg_class c JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = 'orders';"
# What is the session's search_path?
psql -d appdb -c "SHOW search_path;"
# List tables visible in the current schema scope
psql -d appdb -c "\dt" schema | relname | relkind
-----------+---------+---------
reporting | orders | r <- exists in 'reporting', not on default search_path
# search_path:
"$user", public <- 'reporting' is missing here
If pg_class shows the relation under a schema absent from search_path, the
table exists β the session just is not looking there.
-
If the table is genuinely missing, run the migration that creates it (do not hand-create production schema):
# run your migration tool, e.g.: # flyway migrate / alembic upgrade head / dbmate up
-
If it exists in another schema, either schema-qualify the query or set the role's
search_pathso the schema is found:ALTER ROLE app_user SET search_path = app, public; -- persistent per role -- or qualify explicitly: SELECT * FROM reporting.orders;
-
For a case-sensitivity mismatch, quote the name to match how it was created, or recreate it unquoted (lower case) and update queries.
-
Confirm you are connected to the correct database before deeper digging.
psql -d appdb -c "SELECT to_regclass('orders');" -- non-null when resolvable
psql -d appdb -c "SELECT count(*) FROM orders;" -- query now succeeds- Apply schema changes only through versioned migrations run in every environment.
- Set a deterministic
search_pathper role rather than relying on the default. - Use lower-case, unquoted identifiers consistently.
- Add a post-migration smoke test that selects from the tables the app needs.
postgresql Β· schema Β· search-path Β· migrations Β· production