PostgreSQL Identifiers

PostgreSQL Error 42703: Column Does Not Exist (Fix Guide)

Fix PostgreSQL 42703 column does not exist. The #1 real-world cause: unquoted camelCase identifiers get lowercased. Plus typos, alias scope, and ORM gotchas.

Jun 10, 2026 9 min read

The Error Message

ERROR: column "createdat" does not exist
LINE 1: SELECT createdAt FROM users;
HINT: Perhaps you meant to reference the column "users.createdAt".

Look closely at that example — it explains the single most common cause of 42703. You wrote createdAt; the error complains about createdat, all lowercase. PostgreSQL silently lowercased your identifier before looking it up. If the error message shows a different casing than what you typed, you already know the fix: double quotes.

The #1 Cause: Unquoted camelCase Identifiers

PostgreSQL folds every unquoted identifier to lowercase (note: the SQL standard says uppercase — PostgreSQL deliberately differs, and Oracle folds to uppercase instead). So if your table was created with quoted camelCase columns — which is exactly what Prisma does by default, and what Sequelize does without underscored: true — the column is stored as createdAt with a capital A, and only the exact-case quoted form can reach it:

-- The table (as Prisma creates it):
CREATE TABLE users (
  id SERIAL PRIMARY KEY,
  "createdAt" TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- BAD: unquoted, PostgreSQL lowercases it to createdat
SELECT createdAt FROM users;
-- ERROR: column "createdat" does not exist

-- FIXED: double-quote mixed-case identifiers, exact case
SELECT "createdAt" FROM users;

This is why hand-written SQL, BI tools, and AI-generated queries break against ORM-created schemas: the application's ORM always emits the quotes, but anything outside the ORM forgets them. The reverse trap exists too — SELECT "CREATEDAT" fails because quoted identifiers must match the stored case exactly.

All Common Causes (Ranked)

1. Unquoted camelCase / mixed-case column

As above: createdAt unquoted becomes createdat. Telltale sign: the casing in the error differs from what you wrote, often with a HINT naming the real column.

2. Plain typo or convention mismatch

user_name vs username, created_at vs createdAt — the column exists under a slightly different name.

3. SELECT alias referenced in WHERE

WHERE runs before the SELECT list, so the alias doesn't exist yet at filter time.

4. Wrong table alias in a join

u.total when the column belongs to the orders alias. The HINT usually suggests the right qualified name.

5. Missing migration in this environment

The column was added in dev but the migration never ran here. The schema, not the query, is wrong.

6. Double quotes around a string value

In PostgreSQL "John" is always an identifier. WHERE name = "John" means "compare to a column called John" — 42703.

Quick Diagnosis

Ask PostgreSQL for the column's exact stored name — case included:

-- The authoritative list (case shown exactly as stored):
SELECT column_name, data_type
FROM   information_schema.columns
WHERE  table_name = 'users'
ORDER  BY ordinal_position;

-- In psql:
-- \d users

-- Find a column by approximate name across all tables:
SELECT table_name, column_name
FROM   information_schema.columns
WHERE  lower(column_name) = lower('createdAt');

Decision rule: if column_name contains any uppercase character, you must double-quote it in every query. If it's all lowercase, never quote it and write it lowercase. Also read the error's HINT: line — PostgreSQL frequently names the column it thinks you meant, including the correct qualifier.

How to Fix It

Fix 1: Quote mixed-case identifiers (or stop creating them)

-- BAD:
SELECT id, createdAt, updatedAt FROM "Order";
-- ERROR: column "createdat" does not exist

-- FIXED (quote every mixed-case identifier, exact case):
SELECT id, "createdAt", "updatedAt" FROM "Order";

-- BEST long-term: migrate to snake_case so quoting is never needed
ALTER TABLE "Order" RENAME COLUMN "createdAt" TO created_at;
ALTER TABLE "Order" RENAME COLUMN "updatedAt" TO updated_at;
ALTER TABLE "Order" RENAME TO orders;

Note that MySQL behaves differently — column names there are case-insensitive on comparison, which is why queries ported from MySQL break on PostgreSQL. The MySQL counterpart of this error is Error 1054: unknown column.

Fix 2: Don't filter on a SELECT alias in WHERE

-- BAD:
SELECT price * quantity AS line_total
FROM   order_items
WHERE  line_total > 100;
-- ERROR: column "line_total" does not exist

-- FIXED (repeat the expression):
SELECT price * quantity AS line_total
FROM   order_items
WHERE  price * quantity > 100;

-- FIXED (CTE — alias becomes a real column for the outer query):
WITH totals AS (
  SELECT price * quantity AS line_total FROM order_items
)
SELECT * FROM totals WHERE line_total > 100;

Fix 3: Qualify with the correct table alias

-- BAD: total lives on orders
SELECT u.name, u.total
FROM   users u JOIN orders o ON o.user_id = u.id;
-- ERROR: column u.total does not exist
-- HINT: Perhaps you meant to reference the column "o.total".

-- FIXED:
SELECT u.name, o.total
FROM   users u JOIN orders o ON o.user_id = u.id;

Fix 4: Use single quotes for strings

-- BAD: double quotes make "active" an identifier
SELECT * FROM users WHERE status = "active";
-- ERROR: column "active" does not exist

-- FIXED:
SELECT * FROM users WHERE status = 'active';

Fix 5: Apply the missing migration

-- Confirm absence first:
SELECT 1 FROM information_schema.columns
WHERE  table_name = 'users' AND column_name = 'last_login_at';

-- Then run your migration tool, or:
ALTER TABLE users ADD COLUMN last_login_at TIMESTAMPTZ;

ORM Gotchas: Prisma, Sequelize, TypeORM

  • Prisma creates columns with the exact casing of your model fields — camelCase fields become quoted camelCase columns. prisma.$queryRaw and any external SQL must quote them. The clean fix: createdAt DateTime @map("created_at") plus @@map("users") on the model, keeping code camelCase and the database snake_case.
  • Sequelize with default settings also creates camelCase columns. Set underscored: true (per model or globally) so createdAt maps to created_at.
  • TypeORM ships a default naming strategy that preserves property casing; use SnakeNamingStrategy from typeorm-naming-strategies to get snake_case columns.
  • BI tools / SQL editors / AI-generated SQL hitting an ORM-created schema are the classic 42703 factory: the schema has quoted camelCase columns and the generated SQL is unquoted. Either rename to snake_case once, or make sure every consumer quotes correctly.

How to Prevent This Error

Standardize on snake_case for everything in PostgreSQL — unquoted snake_case identifiers are immune to case folding. Configure your ORM's naming mapping on day one (@map / underscored: true / SnakeNamingStrategy) rather than retrofitting it. Verify column names against information_schema.columns before shipping raw SQL. And if your error is about the table rather than the column, see 42P01: relation does not exist — the same case-folding rules apply to table names. For parse-level failures, see 42601: syntax error.

Fix PostgreSQL Errors with AI2SQL

AI2SQL reads your actual schema — including quoted camelCase columns — and generates PostgreSQL queries with correct quoting automatically. Describe what you need in plain English and skip the case-folding archaeology.

Try AI2SQL Free

No credit card required

Frequently Asked Questions

Why does PostgreSQL say my camelCase column does not exist?

PostgreSQL folds every unquoted identifier to lowercase. If the column was created as "createdAt" (quoted, by Prisma or another ORM), then SELECT createdAt actually asks for createdat — which does not exist. Wrap mixed-case identifiers in double quotes: SELECT "createdAt" FROM users. The error's hint often gives it away.

How do I check a column's exact name and case in PostgreSQL?

Query information_schema: SELECT column_name FROM information_schema.columns WHERE table_name = 'users'. The output shows the stored name exactly — if it contains uppercase letters, every query must double-quote it. In psql, \d users does the same job.

Can I use a column alias in the WHERE clause in PostgreSQL?

No. WHERE is evaluated before the SELECT list, so the alias does not exist yet and PostgreSQL raises 42703. Repeat the expression in WHERE, or wrap the query in a subquery or CTE and filter in the outer query. Aliases are allowed in ORDER BY and GROUP BY.

Should I use camelCase or snake_case column names in PostgreSQL?

Use snake_case. Unquoted snake_case identifiers survive PostgreSQL's lowercase folding unchanged, so you never need quotes. CamelCase columns must be double-quoted in every single query forever. In Prisma, keep camelCase in your models but add @map("created_at") to map to snake_case columns; in Sequelize set underscored: true.

Stop Debugging SQL Errors Manually

Describe what you need in plain English. AI2SQL generates correct PostgreSQL queries instantly.

Try AI2SQL Free

No credit card required