InletDownload

PostgreSQL error 42P01

relation does not exist

PostgreSQL found no table, view or sequence with that exact name in the schemas it searched. Usually the name was created with capitals in double quotes, the table is in a schema that isn’t on your search_path, or you’re connected to a different database.

ERROR:  relation "customers" does not exist

Tested on PostgreSQL 18.6 (also 14–17) · Updated 9 October 2026

What it means

“Relation” is PostgreSQL’s word for anything table-like: tables, views, materialised views, sequences, indexes and foreign tables. The server looked up the name in your statement and found nothing with that exact spelling in any schema it searched.

The lookup happens before any row is read, so nothing ran. The caret under LINE 1 points at the name it couldn’t find. DROP TABLE says table "x" does not exist instead; it’s the same error code and the same causes.

Two related messages share the code. missing FROM-clause entry for table "c" means the query uses c.something but no table or alias c is in its FROM clause: add the table, or fix the alias. invalid reference to FROM-clause entry for table "customers" means the table has an alias, so it must be called by that alias (c.email, not customers.email); the hint names it.

Common causes

  1. Capitals and double quotes. PostgreSQL folds unquoted names to lower case. A table created as "Customers" (common with ORMs, GUI tools and tables migrated from MySQL or SQL Server) can only be reached as "Customers"; Customers and customers both become customers.
  2. The table is in a schema that isn’t on your search_path, such as billing.invoices when the path is the default "$user", public.
  3. You’re connected to a different database, server or port. Each database has its own tables, and a connection can’t see another database’s tables. Connecting to the default postgres database by mistake is the classic case.
  4. A typo or an old name: singular for plural, the name before a rename, or a sequence that kept its original name (bills_id_seq after bills was renamed to invoices).
  5. You have no USAGE on the schema. Schemas you can’t use are skipped during the search_path lookup, so the table looks missing. Qualify the name and you get permission denied for schema instead.
  6. The table doesn’t exist yet: a migration that hasn’t run, failed, or ran against another database; a table created in another session that hasn’t committed; another session’s temporary table.

How to fix it

Find where the table really is

Search every schema, ignoring case:

SELECT table_schema, table_name
FROM information_schema.tables
WHERE table_name ILIKE 'customers';

information_schema lists only tables you have some privilege on. In psql, \dt *.customers lists the tables called customers in every schema; it folds the pattern to lower case, so write \dt *."Customers" for a capitalised name (see psql commands). No rows at all? Check where you are:

SELECT current_database(), current_user, current_schema();
SHOW search_path;

Quote names that have capitals

If the table shows up as Customers with a capital, quote it exactly, every time:

SELECT * FROM "Customers";

To stop quoting, rename it once to lower case (update any code that already quotes it):

ALTER TABLE "Customers" RENAME TO customers;

Qualify the schema, or add it to the search path

SELECT * FROM billing.invoices;

-- for this session
SET search_path = billing, public;

-- for every new session of one role
ALTER ROLE app SET search_path = billing, public;

ALTER ROLE … SET takes effect at the next connection, not in sessions already open.

Use the sequence’s real name

Don’t guess a sequence name from the table name. Ask for it:

SELECT pg_get_serial_sequence('invoices', 'id');
SELECT nextval(pg_get_serial_sequence('invoices', 'id'));

Grant access to the schema

If the table is in a schema your role can’t use, the owner or a superuser grants it:

GRANT USAGE ON SCHEMA billing TO app;
GRANT SELECT ON ALL TABLES IN SCHEMA billing TO app;

After that a missing table privilege shows up as permission denied, not as “does not exist”.

Reproduce it

On PostgreSQL 18.6, in a scratch schema:

CREATE TABLE "Customers" (id int PRIMARY KEY, name text);
SELECT * FROM Customers;
ERROR:  relation "customers" does not exist
LINE 1: SELECT * FROM Customers;
                      ^

SELECT * FROM "Customers"; works. The opposite mistake fails the same way: the table invoices is lower case, and the quoted name has a capital:

ERROR:  relation "Invoices" does not exist
LINE 1: SELECT * FROM "Invoices";
                      ^

A table that exists in seo_pgsql, queried with the default search path:

SET search_path = "$user", public;
SELECT * FROM invoices;
SELECT count(*) FROM seo_pgsql.invoices;
ERROR:  relation "invoices" does not exist
LINE 1: SELECT * FROM invoices;
                      ^

The qualified seo_pgsql.invoices works. A sequence that kept its old name after the table was renamed from bills to invoices:

ERROR:  relation "invoices_id_seq" does not exist
LINE 1: SELECT nextval('invoices_id_seq');
                       ^

pg_get_serial_sequence('invoices', 'id') returned seo_pgsql.bills_id_seq. And as a role without USAGE on the schema, the unqualified name “does not exist” while the qualified one is refused:

ERROR:  relation "invoices" does not exist
ERROR:  permission denied for schema seo_pgsql

PostgreSQL 14, 15, 16 and 17 print exactly the same messages.

In Inlet

The query editor completes table names from the live schema, so a name you pick from the list is one that exists on the database you’re connected to. When a statement fails, Inlet shows the error at the position the server reports. With your own Anthropic API key, Ask Claude (⌘L) can fix the failed statement; it sends the schema and the SQL, never rows.

Related

Sources