Blog
3 min read

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