ERROR: relation "customers" does not exist when you can see a table called Customers means the table was created with a quoted, mixed-case name and your query does not quote it. PostgreSQL folds unquoted names to lower case, so write "Customers" with the quotes, exactly as it was created.

Look closely at the message: you typed Customers, the error says customers. That lower-case name in the error is the whole clue. Quote the identifier in every query, or rename the table to lower case once and stop quoting.

The error

docker exec fix4-pg18 psql -U postgres -d appdb -v VERBOSITY=verbose -f /tmp/e4-select.sql
psql:/tmp/e4-select.sql:1: ERROR:  42P01: relation "customers" does not exist
LINE 1: SELECT * FROM Customers;
                      ^
LOCATION:  parserOpenTable, parse_relation.c:1466

The file contains one line, SELECT * FROM Customers;. PostgreSQL 18.4 printed no hint with it, so the case change in the relation name is the only thing pointing at the cause.

Why it happens

The SQL standard says unquoted identifiers are case-insensitive, and PostgreSQL implements that by folding them to lower case before it looks them up. A quoted identifier is taken literally. The table here was created with CREATE TABLE "Customers" (id int, name text);, so its stored name is Customers with a capital C, and \dt lists it that way. The query SELECT * FROM Customers asks for customers, which is a different name, and code 42P01 ("undefined table") follows. SQL Server developers meet this most, because a default SQL Server collation would treat both spellings as the same table.

The usual source of quoted mixed-case names is an ORM. EF Core with the Npgsql provider keeps the C# names by default and quotes them in the SQL it generates, so a DbSet<Customer> Customers becomes a table called "Customers". EF Core's own queries always quote it and work; the error appears when you open psql, a reporting tool or a raw SQL string and type the name without quotes.

The fix

docker exec fix4-pg18 psql -U postgres -d appdb -v VERBOSITY=verbose -f /tmp/e4-fix.sql
 id |     name
----+--------------
  1 | Maria Garcia
  2 | David Chen
(2 rows)

The file now holds SELECT * FROM "Customers";, the identifier quoted exactly as it was created. fix4-pg18 and appdb are the test's own names. Quoting has to be exact: "customers" or "CUSTOMERS" would fail the same way. The longer-term fix is to stop creating mixed-case names: unquoted lower-case or snake_case tables (customers, order_lines) work with or without quotes in every tool. For EF Core that means configuring the table and column names, for example with a naming-convention package, before the first migration rather than after.

How it was reproduced

A fresh postgres:18 container (PostgreSQL 18.4, Debian build) on Docker Desktop 4.91.0 under Windows 11, database appdb. The table was created and filled with two fictional rows from a SQL file run with psql -f; the query files were copied into the container with docker cp, because Windows PowerShell 5.1 strips double quotes from arguments passed to native programs, which would have removed the very quotes this post is about.

Frequently asked

Why does PostgreSQL say relation does not exist when the table exists?
Most often the table was created with a quoted mixed-case name such as "Customers" and the query uses it unquoted. PostgreSQL folds unquoted names to lower case, so it looks for customers and does not find it.
Are PostgreSQL table names case sensitive?
Quoted names are case sensitive and stored exactly as written. Unquoted names are folded to lower case, which makes them behave as case insensitive as long as you never quote them.
How do I query an EF Core table with a capital letter in psql?
Put the name in double quotes exactly as EF Core created it, for example SELECT * FROM "Customers". Column names created by EF Core need the same quoting.

More decoded errors in the Fixes category. For a naming rule that avoids this on both PostgreSQL and SQL Server, see naming conventions: snake_case, plurals and case sensitivity.