PostgreSQL Error 42P16: invalid table definition
PostgreSQL error 42P16 (invalid_table_definition) is raised when a CREATE TABLE or ALTER TABLE statement is syntactically valid but semantically broken — meaning the table's structure, constraints, or inheritance configuration is logically inconsistent. Unlike a plain syntax error (42601), this error indicates that PostgreSQL understood your SQL but found it impossible to build a coherent table definition from it.
Top 3 Causes
1. Duplicate PRIMARY KEY Definitions
Defining a primary key at both the column level and the table level is the most common trigger for this error.
Broken:
-- ERROR: multiple primary keys for table "users" are not allowed
CREATE TABLE users (
user_id SERIAL PRIMARY KEY, -- column-level PK
email TEXT NOT NULL,
CONSTRAINT pk_users PRIMARY KEY (user_id) -- table-level PK (conflict!)
);
Fixed:
-- Keep only one PRIMARY KEY definition
CREATE TABLE users (
user_id SERIAL PRIMARY KEY,
email TEXT NOT NULL
);
-- OR use table-level constraint (preferred for composite keys)
CREATE TABLE users (
user_id SERIAL,
email TEXT NOT NULL,
CONSTRAINT pk_users PRIMARY KEY (user_id)
);
2. Column Type Conflict in Table Inheritance
When using PostgreSQL's INHERITS clause, the child table must not redefine a parent column with a different data type.
Broken:
CREATE TABLE vehicles (
vehicle_id INT PRIMARY KEY,
plate_no VARCHAR(20) NOT NULL
);
-- ERROR: column "plate_no" has a type conflict
CREATE TABLE trucks (
plate_no TEXT, -- type mismatch with parent VARCHAR(20)
payload NUMERIC
) INHERITS (vehicles);
Fixed:
-- Remove the conflicting column from the child table
-- and let it inherit from the parent automatically
CREATE TABLE trucks (
payload NUMERIC
) INHERITS (vehicles);
-- Verify inherited structure
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_name = 'trucks';
3. Invalid Partition Bound Specification
Using the wrong bound syntax (e.g., IN for a RANGE partition) or overlapping partition ranges causes this error in declarative partitioning.
Broken:
CREATE TABLE events (
event_id BIGINT,
event_date DATE NOT NULL
) PARTITION BY RANGE (event_date);
-- ERROR: invalid bound specification for a range partition
CREATE TABLE events_2024
PARTITION OF events
FOR VALUES IN ('2024-01-01'); -- LIST syntax on a RANGE partition
Fixed:
CREATE TABLE events (
event_id BIGINT,
event_date DATE NOT NULL
) PARTITION BY RANGE (event_date);
-- Correct RANGE partition children
CREATE TABLE events_2024_h1
PARTITION OF events
FOR VALUES FROM ('2024-01-01') TO ('2024-07-01');
CREATE TABLE events_2024_h2
PARTITION OF events
FOR VALUES FROM ('2024-07-01') TO ('2025-01-01');
-- Inspect partition layout
SELECT child.relname AS partition,
pg_get_expr(child.relpartbound, child.oid) AS bounds
FROM pg_inherits
JOIN pg_class child ON pg_inherits.inhrelid = child.oid
JOIN pg_class parent ON pg_inherits.inhparent = parent.oid
WHERE parent.relname = 'events';
Quick Fix Checklist
- ✅ Ensure only one
PRIMARY KEYconstraint exists per table. - ✅ When using
INHERITS, do not redefine parent columns with different types in child tables. - ✅ Match your partition bound syntax (
FROM/TOfor RANGE,INfor LIST) to the parent'sPARTITION BYstrategy. - ✅ Always test DDL on a staging environment before applying to production.
- ✅ Use
\d+ table_nameinpsqlto inspect the final table structure after creation.
Prevention Tips
Validate DDL before production deployment.
Run every CREATE TABLE or ALTER TABLE script in a development or staging environment first. Use pg_dump --schema-only to snapshot your current schema and diff it after changes.
-- Quick constraint audit for any table
SELECT conname, contype, pg_get_constraintdef(oid)
FROM pg_constraint
WHERE conrelid = 'your_table'::regclass;
Document and test partition strategies upfront.
Partition strategy (RANGE, LIST, HASH) and partition key choices are expensive to change after the fact. Sketch out all partition boundaries in a design document, prototype with a small dataset, and verify with the system catalog before scaling out.
Related Errors
| Code | Name | Notes |
|---|---|---|
42601 |
syntax_error |
Malformed SQL; fix syntax before investigating 42P16 |
42P07 |
duplicate_table |
Table already exists; use CREATE TABLE IF NOT EXISTS
|
0A000 |
feature_not_supported |
Often appears alongside 42P16 for unsupported partition configs |
23514 |
check_violation |
CHECK constraint failure at data insert time, not table definition |
📖 Want a more detailed guide?
Check out the full in-depth version (Korean) on oraerror.com — includes detailed analysis, additional SQL examples, and prevention tips.
Top comments (0)