Postgres "ERROR: relation does not exist": Causes and Fixes
Postgres can't find the table you named. The usual causes — migrations not run, connected to the wrong database, uppercase names and double quotes, the table is in a different schema, or permissions — and how to check each with psql.
ERROR: relation "users" does not exist
LINE 1: SELECT * FROM users
In Postgres, a relation is a table (or view). This error means Postgres looked for a table called users and didn't find one — where it looked. That last part is the key: the table often does exist, just not in the database, schema or spelling Postgres used.
Cause 1: the migrations haven't run
The table is defined in your code but was never created in this database — typical right after setting up a new environment or deploying for the first time.
Run your migrations:
npx prisma migrate deploy # Prisma
npx drizzle-kit migrate # Drizzle
python manage.py migrate # Django
alembic upgrade head # SQLAlchemy/Alembic
(Database migrations explained, Prisma migrate in production)
Cause 2: connected to the wrong database
Your app's connection string points at a different database than the one you created the table in — postgres instead of myapp, a local database instead of the remote one, staging instead of production.
Check what you're actually connected to:
SELECT current_database(), current_user, inet_server_addr();
And list the tables there (in psql):
\dt
(Postgres connection strings, How to view your Postgres database)
Cause 3: uppercase letters and double quotes
Postgres converts unquoted names to lowercase. But names created in double quotes keep their exact case.
ORMs like Prisma often create tables with quoted, capitalised names such as "User". Then:
SELECT * FROM User; -- Postgres looks for user (lowercase) → error
SELECT * FROM "User"; -- ✅
If \dt shows a table name with capitals, you must quote it every time. (Or map models to lowercase table names with @@map("users") in Prisma.)
Cause 4: the table is in a different schema
Postgres databases contain schemas (namespaces). By default it searches the public schema. If the table lives in app or auth:
SELECT * FROM app.users;
See all tables in all schemas:
\dt *.*
or
SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_name ILIKE 'users';
You can change the search path for a role: ALTER ROLE app_user SET search_path = app, public;
Cause 5: the user can't see it
If your database user lacks permission on the schema, Postgres may report the table as missing rather than forbidden. Check grants, especially when your app uses a different, restricted user from the one that created the tables. (Postgres roles and permissions)
Cause 6: a typo or plural mismatch
user vs users, order_items vs orderitems. The error message shows the exact name it looked for — compare it with \dt.
Cause 7: tests or a fresh container
A test database or a new Docker container starts empty every time. Make sure your test setup runs migrations before the tests. (Docker Compose for local development)
Quick diagnosis in psql
\conninfo -- which database and user am I?
\dn -- list schemas
\dt *.* -- list all tables everywhere
\d "User" -- describe a table with an exact-case name
EasySpawn gives each app its own Postgres database on your server and runs your migrations as part of the deploy, with Claude Code on hand to inspect the schema when something's missing. See how it works or join the waitlist.
Related: Database Migrations Explained · How to View Your Postgres Database · Postgres Connection Strings Explained · What Is PostgreSQL?
Keep reading
Postgres "password authentication failed for user": How to Fix It
Postgres rejected the username or password your app sent. The real causes — wrong password in the connection string, special characters not URL-encoded, a Docker volume that kept an old password, the wrong user or host, pg_hba.conf rules — and how to reset the password safely.
NULL in SQL Explained: Why = NULL Never Works
NULL in SQL means 'unknown', not zero or an empty string — which is why WHERE column = NULL returns nothing. How IS NULL, COALESCE, NULLIF and IS DISTINCT FROM work, how NULL affects counts, sums, NOT IN and unique constraints, and when to use NOT NULL.