PostgreSQL Error 42611: invalid_column_definition
PostgreSQL error code 42611 (invalid_column_definition) is raised when a column definition in a CREATE TABLE, ALTER TABLE, or composite type statement contains invalid syntax, an unsupported data type, or an illegal combination of constraints. This error is common during database migrations from other RDBMS platforms or when developers use incorrect PostgreSQL syntax. Identifying the exact cause early saves significant debugging time.
Top 3 Causes & Fixes
1. Using Unsupported or Non-PostgreSQL Data Types
Developers migrating from Oracle or MySQL often use platform-specific types that PostgreSQL does not recognize, triggering error 42611.
-- ❌ Wrong: Oracle/MySQL-specific types
CREATE TABLE employees (
id NUMBER(10), -- Oracle only
name VARCHAR2(100), -- Oracle only
active TINYINT -- MySQL style
);
-- ERROR: 42611 invalid column definition
-- ✅ Correct: PostgreSQL-compatible types
CREATE TABLE employees (
id NUMERIC(10), -- PostgreSQL equivalent
name VARCHAR(100), -- Standard SQL / PostgreSQL
active BOOLEAN -- PostgreSQL boolean
);
-- ✅ If using custom types, create them first
CREATE TYPE emp_status AS ENUM ('active', 'inactive');
CREATE TABLE employees (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
status emp_status DEFAULT 'active'
);
2. Invalid Constraint Syntax or Illegal Constraint Combinations
Using wrong constraint syntax—such as missing parentheses in CHECK, unsupported DEFAULT expressions, or incorrect GENERATED column syntax—causes this error.
-- ❌ Wrong: Missing parentheses in CHECK, wrong DEFAULT function
CREATE TABLE orders (
order_id SERIAL,
order_date TIMESTAMP DEFAULT GETDATE(), -- SQL Server function
amount NUMERIC(10,2) CHECK amount > 0 -- Missing parentheses
);
-- ✅ Correct: PostgreSQL syntax
CREATE TABLE orders (
order_id SERIAL PRIMARY KEY,
order_date TIMESTAMP DEFAULT NOW(),
amount NUMERIC(10,2) CHECK (amount > 0)
);
-- ❌ Wrong: Generated column missing STORED keyword
CREATE TABLE products (
price NUMERIC(10,2),
tax NUMERIC(4,2),
total NUMERIC(10,2) GENERATED ALWAYS AS (price * (1 + tax))
);
-- ✅ Correct: Include STORED keyword
CREATE TABLE products (
price NUMERIC(10,2),
tax NUMERIC(4,2),
total NUMERIC(10,2) GENERATED ALWAYS AS (price * (1 + tax)) STORED
);
3. Invalid Precision, Scale, or Length Values
Specifying a scale greater than precision for NUMERIC, or a zero/negative length for VARCHAR/CHAR, will trigger 42611.
-- ❌ Wrong: Scale exceeds precision, zero-length VARCHAR
CREATE TABLE bad_definitions (
price NUMERIC(3, 5), -- Scale(5) > Precision(3): invalid
code VARCHAR(0), -- Zero length: invalid
flag CHAR(-1) -- Negative length: invalid
);
-- ✅ Correct: Valid precision, scale, and length values
CREATE TABLE good_definitions (
price NUMERIC(10, 5), -- Precision(10) >= Scale(5)
code VARCHAR(20), -- Positive length
flag CHAR(1) -- Positive length
);
-- ✅ Well-structured real-world example
CREATE TABLE transactions (
id BIGSERIAL PRIMARY KEY,
ref_code CHAR(8) NOT NULL,
description VARCHAR(500),
amount NUMERIC(15, 4) NOT NULL DEFAULT 0,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
is_active BOOLEAN NOT NULL DEFAULT TRUE
);
Quick Fix Checklist
-
Check your data types: Make sure all types exist in PostgreSQL (
pg_catalog.pg_type). - Verify custom types exist: ENUM or DOMAIN types must be created before use.
-
Add parentheses to CHECK: Always wrap CHECK expressions in
(). -
Use
NOW()notGETDATE(): Replace DBMS-specific functions with PostgreSQL equivalents. -
Validate precision/scale: Ensure scale ≤ precision for
NUMERIC, and length > 0 for character types. -
Add
STOREDto generated columns: PostgreSQL requires theSTOREDkeyword explicitly.
Prevention Tips
1. Wrap DDL in a transaction for safe testing:
BEGIN;
CREATE TABLE my_new_table (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
score NUMERIC(6, 2) CHECK (score >= 0),
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
-- Inspect if needed, then:
ROLLBACK; -- Change to COMMIT when ready to apply
2. Verify available types before writing DDL:
-- Check if a custom type already exists
SELECT typname, typtype
FROM pg_catalog.pg_type
WHERE typname = 'emp_status'
AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'public');
Related Errors
| Code | Name | When It Occurs |
|---|---|---|
42601 |
syntax_error |
General SQL syntax errors, often appears alongside 42611 |
42P16 |
invalid_table_definition |
Table-level definition errors (e.g., bad partition setup) |
42804 |
datatype_mismatch |
Type mismatch during insert/cast after bad column definition |
22023 |
invalid_parameter_value |
Out-of-range precision or length values for a type |
📖 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)