Incident brief
Relation already exists
A CREATE TABLE named a relation that already exists. PostgreSQL refused to create a second object under the same name instead of silently overwriting it.
What lands in your log
ERROR: relation "widgets" already exists
In 10 seconds
- What triggers it
- CREATE TABLE with a name that already exists in the current schema.
- Fix
- Use CREATE TABLE IF NOT EXISTS if the script is meant to be safely re-runnable.
- Proof
- Reproduced on PostgreSQL 16.14 → Creating a table with a name that already existed was rejected with SQLSTATE 42P07. The table was confirmed unchanged afterward.
Fix
What to do right now
Application-level steps for this error.
- Use CREATE TABLE IF NOT EXISTS if the script is meant to be safely re-runnable.
- Otherwise, rename the new table or drop the old one first, deliberately.
- Don't assume IF NOT EXISTS updates an existing table's columns, see the premium test for what it actually does.
CREATE TABLE IF NOT EXISTS inventory.widgets (id serial PRIMARY KEY, note text);For this error
See this error live on the server
Run these against the affected instance to confirm the diagnosis before you act.
Resolve the existing relation and its relkind before deciding to reuse, rename, or drop it.
Existing relation with this name
Replace the literal with the identifier from the error; relkind distinguishes table, index, view, sequence, and partition.
SELECT n.nspname AS schema_name, c.relname, c.relkind
FROM pg_class c
JOIN pg_namespace n ON n.oid = c.relnamespace
WHERE c.relname = '<relation_name>'
ORDER BY n.nspname, c.relname
LIMIT 50;Why it happens
What PostgreSQL is telling you
The mechanism behind the error, grounded in the official manual, not paraphrased.
PostgreSQL 16 Documentation, CREATE TABLE, IF NOT EXISTS
Do not throw an error if a relation with the same name already exists. A notice is issued in this case. Note that there is no guarantee that the existing relation is anything like the one that would have been created.Read the full section on postgresql.org →
The duplicate CREATE TABLE
PostgreSQL found an existing relation with the same name in the current schema and refused to create a second one under it.Confirming the original table is unchanged
The original table is exactly as it was, empty, with no side effect from the rejected CREATE TABLE.Reproduce & verify
A real, single-session PostgreSQL reproduction
A literal transcript of SQL run against a live PostgreSQL instance in an isolated lab. The commands below are exactly what was executed.
- 1CREATE TABLE with a name that already exists in the current schema.
- 2PostgreSQL refuses to create a duplicate relation.
- 3SQLSTATE 42P07 is reported, naming the exact relation.
One client: create a table, then try to create another table with the same name.
CREATE SCHEMA IF NOT EXISTS inventory;
DROP TABLE IF EXISTS inventory.widgets;
CREATE TABLE inventory.widgets (id serial PRIMARY KEY, note text);CREATE TABLE inventory.widgets (id serial PRIMARY KEY, note text);SELECT count(*) FROM inventory.widgets;What PostgreSQL actually returned
DROP TABLE
CREATE TABLEERROR: relation "widgets" already exists count
-------
0
(1 row)The IF NOT EXISTS fix was tested directly against the same duplicate-name scenario.
Without this
Above: a second CREATE TABLE with the same name is rejected.
With this, tested
Below: the same CREATE TABLE, with IF NOT EXISTS, succeeds with a notice instead of an error.
- A second operational test: exact SQL, raw output, measured result, and engineer notes
- Fix that hides the error but doesn't apply the new schema: exact SQL, output, and verdict
- A manual-grounded production interpretation of the lab result
Card required. Cancel before day 7 and you are not charged.
Runbook to fix this
Runbooks for this incident
Full step-by-step fixes for the condition behind this error: the diagnosis, the exact SQL, and output captured in the lab.
- Eliminate redundant and duplicate indexesWrites are slow and the same leading column is indexed three different ways.Pro
- Rebuild a bloated index with REINDEX CONCURRENTLYThe index is four times the size of its entries and scans keep getting slower.Pro
- Recover from a failed CREATE INDEX CONCURRENTLYAn invalid index left behind by a failed build: reads ignore it, writes still pay for it.Pro
Connected
Everything this error touches
Every page this SQLSTATE connects to: the concept that explains it, the runbooks that fix it, the parameters you tune to prevent it, and the sibling errors it travels with. All real cross-references. Jump straight in, or open the full interactive map.
Fix it — runbooks
Related errors
Verification
- Last verified
- 2026-07-15 (Docker lab, PostgreSQL 16.14)
- Verification scope
- Verified against PostgreSQL 16.14 in an isolated lab environment
- Audit status
- reviewed
Went further?
Pro unlocks the second lab proof
Free page stops the bleeding. Pro adds the operational test, SQLSTATE audit, and deeper evidence, same error, more certainty.