ERROR: relation "customers" already exists — a CREATE statement named a table that is already in the schema. In a script you run more than once, write CREATE TABLE IF NOT EXISTS. Under EF Core, the table exists but __EFMigrationsHistory does not list the migration that created it, so make the history table tell the truth.

dotnet ef migrations list shows the problem at a glance: a migration marked (Pending) whose tables are already there. Record it as applied with one INSERT into the history table, but only after checking that the schema really matches that migration.

The error

CREATE TABLE customers (id int PRIMARY KEY, name text NOT NULL, email text UNIQUE);
ERROR:  relation "customers" already exists

The EF Core version, from dotnet ef database update:

Applying migration '20261030025248_InitialCreate'.
Failed executing DbCommand (3ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
CREATE TABLE "Customers" (
    "Id" integer GENERATED BY DEFAULT AS IDENTITY,
    "Name" text NOT NULL,
    "Email" text,
    CONSTRAINT "PK_Customers" PRIMARY KEY ("Id")
);
Npgsql.PostgresException (0x80004005): 42P07: relation "Customers" already exists

Why it happens

"Relation" is PostgreSQL's catalog word for tables, indexes, views and sequences, which is why a repeated CREATE INDEX names a relation too. A plain CREATE never replaces anything, so running the same script twice fails at the first object it creates.

EF Core decides what to run by reading __EFMigrationsHistory, not by looking at the tables. If a migration's row is missing, EF Core treats it as pending and runs its CREATE TABLE again. The test broke the history two ways and got the same error from both: by deleting the row, and by copying the database with pg_dump --schema-only, which created an empty history table in the copy (zero rows) next to a complete Customers table.

The fix

CREATE TABLE IF NOT EXISTS customers (id int PRIMARY KEY, name text NOT NULL, email text UNIQUE);
NOTICE:  relation "customers" already exists, skipping
CREATE TABLE

IF NOT EXISTS checks only the name: it does not compare columns, so a table with an older shape stays as it is. For EF Core, record the migration that the schema already contains, then update:

INSERT INTO "__EFMigrationsHistory" ("MigrationId", "ProductVersion")
VALUES ('20261030025248_InitialCreate', '10.0.12');
INSERT 0 1
PS> dotnet ef database update
Build started...
Build succeeded.
The Entity Framework tools version '10.0.0' is older than that of the runtime '10.0.12'. Update the tools for the latest features and bug fixes. See https://aka.ms/AAc1fbw for more information.
Acquiring an exclusive lock for migration application. See https://aka.ms/efcore-docs-migrations-lock for more information if this takes too long.
No migrations were applied. The database is already up to date.
Done.

The migration id is the file name in your Migrations folder without .cs, and the second value is your EF Core version. Insert rows only for migrations the database really has; a row for one whose changes are missing hides them, and EF Core will not apply them. For the schema-only copy, bring the history rows along instead of typing them:

pg_dump -U postgres --data-only --table='"__EFMigrationsHistory"' shopef | psql -U postgres -d shopef2 -q

After that, dotnet ef database update --connection against the copy also answered No migrations were applied.

How it was reproduced

A postgres:18 container (PostgreSQL 18.4) on Docker 29.9.2 under Windows 11, psql 18.4 inside it, and a .NET 10 console app (SDK 10.0.403) with Npgsql.EntityFrameworkCore.PostgreSQL 10.0.3, Microsoft.EntityFrameworkCore.Design 10.0.12 and the dotnet-ef 10.0.0 tool, whose warning about being older than the 10.0.12 runtime is harmless here. One Customer entity and one migration, InitialCreate, applied and then broken. The names fix7-pg, shop, shopef and shopef2 are this test's own, and the container was removed afterwards.

Frequently asked

How do I fix relation already exists in PostgreSQL?
In a script that may run twice, use CREATE TABLE IF NOT EXISTS or CREATE INDEX IF NOT EXISTS; PostgreSQL prints a NOTICE and skips the object. If the error comes from EF Core migrations, the table is there but the migration is not recorded in __EFMigrationsHistory; record it once you have checked the schema matches.
Why does dotnet ef database update try to create a table that already exists?
EF Core runs every migration that has no row in __EFMigrationsHistory. If the row was deleted, or the database was copied with pg_dump --schema-only, which copies the history table without its rows, the first migration looks pending and its CREATE TABLE runs again. dotnet ef migrations list marks it (Pending).
Does CREATE TABLE IF NOT EXISTS update an existing table's columns?
No. It only checks whether a relation with that name exists and skips the statement if it does, whatever columns the existing table has. Use ALTER TABLE to change an existing table.

More decoded errors in the Fixes category. The SQL Server version of the same EF Core situation is There is already an object named in the database (2714).