Skip to content
Oracle Database

Oracle Error ORA-00904

invalid identifier

Applies to: Oracle Database 19c, 21c, 23ai and 26ai (same message; the documented cause was expanded)
Last technically reviewed 24 October 2026 on Oracle Database Error Messages reference (26ai, 21c and 19c); not run locally
The full message
ORA-00904: "EMAIL_ADDRESS": invalid identifier
Other forms of this message
With a table alias in the reference
ORA-00904: "C"."EMAIL_ADDRESS": invalid identifier
SQL*Plus and SQLcl mark the position with an asterisk
SELECT email_address FROM customers
       *
ERROR at line 1:
ORA-00904: "EMAIL_ADDRESS": invalid identifier
Error code
ORA-00904
Product
Oracle Database
Versions
Oracle Database 19c, 21c, 23ai and 26ai (same message; the documented cause was expanded)
Last reviewed
24 October 2026

What it means

ORA-00904 means a column name or other identifier in the statement does not exist in the tables it refers to, is not visible at that point of the query, or is not a valid Oracle name. The message shows the name Oracle could not resolve.

Oracle checks every column reference against the tables, views and subqueries in the FROM clause. A name that is misspelled, belongs to another table, or is used where it is not in scope cannot be resolved, and the statement fails with ORA-00904 before it runs. The name in the message is the one Oracle tried, in upper case for an unquoted name, which often shows the mistake straight away.

Oracle's current reference also covers the naming rules. A valid unquoted identifier begins with a letter, contains only letters, digits and the characters $, _ and #, and is not a reserved word; any other name must be in double quotes. Unquoted names are stored and compared in upper case, while quoted names keep their exact case. So a column created as "CustomerName" has to be written that way every time. The maximum length is 128 bytes when the COMPATIBLE parameter is 12.2 or higher (the 19c error text still says 30 characters, which was the older limit).

A column alias from the SELECT list is a common trap. Oracle's SQL Language Reference says the alias can be used in ORDER BY but not in other clauses of the same query, and that from Release 23 it can also be used in GROUP BY. Using it in WHERE, or in GROUP BY on 19c, raises ORA-00904.

Common causes

  • The column name is misspelled, or the column does not exist in this version of the table (a migration has not run yet).
  • A SELECT list alias is used in WHERE, HAVING or (before Release 23) GROUP BY of the same query.
  • The column was created with a quoted mixed-case or lower-case name, such as "CustomerName", and is referenced without matching quotes.
  • The column is qualified with the wrong table alias, or the alias is used outside the subquery where it is defined.
  • The name is a reserved word or contains characters that are only allowed inside double quotes, for example in a CREATE TABLE written for another database.

Diagnose it

Read-only These queries only read the data dictionary (USER_, ALL_ and DBA_ views) and dynamic performance views (V$). They do not change data, privileges or settings, so they are safe to run first. DBA_ and V$ views need the matching privileges.

List the real column names of the table

SELECT column_name, data_type
FROM all_tab_columns
WHERE owner = 'SHOP'
  AND table_name = 'CUSTOMERS'
ORDER BY column_id;
What to look for

The columns exactly as stored. Compare them with the name in the message; a column in lower or mixed case here was created in double quotes.

Which tables have a column with a similar name?

SELECT owner, table_name, column_name
FROM all_tab_columns
WHERE owner = 'SHOP'
  AND UPPER(column_name) LIKE '%EMAIL%'
ORDER BY table_name;
What to look for

Where the column really lives and how it is spelt, for example EMAIL instead of EMAIL_ADDRESS, or a column on another table that needs a join.

Is the name a reserved word?

V$RESERVED_WORDS lists the SQL keywords; RESERVED = Y means the word cannot be an unquoted identifier. Querying V$ views needs SELECT_CATALOG_ROLE or a grant from your DBA; otherwise check the reserved words list in the SQL Language Reference.

SELECT keyword, reserved, res_semi
FROM v$reserved_words
WHERE keyword = 'COMMENT';
What to look for

A row with RESERVED = Y means the name must be changed or written in double quotes.

How to fix it

Each fix is labelled with what it changes. Try read-only checks and the least invasive fix first, and test changes outside production.

Use the column name exactly as the dictionary shows it

Read-only

Correct the spelling, or add the join that brings in the table that owns the column, and qualify columns with their table alias so it is clear where each one comes from.

SELECT c.id, c.email
FROM shop.customers c
WHERE c.email LIKE '%@example.com';

Repeat the expression, or wrap the query, instead of using the alias in WHERE

Read-only

An alias exists only after the SELECT list is evaluated. Filter on the expression itself, or compute it in an inline view and filter outside it.

SELECT *
FROM (
    SELECT order_id, quantity * unit_price AS line_total
    FROM shop.order_lines
)
WHERE line_total > 100;

Match the case of a quoted identifier

Read-only

If the column was created as "CustomerName", reference it with the same quotes and case. In new tables, avoid quoted names so the column can be written in any case.

SELECT "CustomerName"
FROM shop.customers;

Rename a column that uses a reserved word or quoted name

Changes schema

When a quoted or reserved name keeps causing errors, rename it once to a plain upper-case name, then update the code that uses it.

Caution: Every view, procedure, report and application query that uses the old name breaks until it is updated, and dependent PL/SQL becomes invalid. Find them first (ALL_DEPENDENCIES and a search of the application code) and change them in the same release.

ALTER TABLE shop.customers RENAME COLUMN "CustomerName" TO customer_name;

Examples

Using a SELECT alias in WHERE

Not reproduced here: no Oracle Database is installed for this site. The result shows the message format from Oracle's error reference with the identifier filled in, as Oracle reports an unquoted name: in upper case and in double quotes. The rule that a column alias cannot be used in WHERE comes from the SELECT page of Oracle's SQL Language Reference.

SELECT order_id, quantity * unit_price AS line_total
FROM order_lines
WHERE line_total > 100;
Expected result (documented format, not run)
ORA-00904: "LINE_TOTAL": invalid identifier

A column created with a quoted mixed-case name

Not reproduced here: no Oracle Database is installed for this site. The case rules come from Oracle's SQL Language Reference (unquoted names are read as upper case, quoted names are case sensitive) and the message format from its error reference. Tools that generate DDL from other databases often quote names like this.

CREATE TABLE customers (
    id             NUMBER PRIMARY KEY,
    "CustomerName" VARCHAR2(100)
);

-- unquoted, so Oracle looks for CUSTOMERNAME
SELECT customername FROM customers;
Expected result (documented format, not run)
ORA-00904: "CUSTOMERNAME": invalid identifier

GROUP BY a column alias on Oracle Database 19c

Not reproduced here: no Oracle Database is installed for this site. Oracle's SQL Language Reference states that a column alias can be used in GROUP BY from Release 23, so this query is accepted on 23ai and 26ai; on 19c and 21c, group by the expression itself. The message format comes from Oracle's error reference.

SELECT EXTRACT(YEAR FROM order_date) AS order_year, COUNT(*)
FROM orders
GROUP BY order_year;
Expected result (documented format, not run)
ORA-00904: "ORDER_YEAR": invalid identifier

How to prevent it

Create tables with plain unquoted names in a single style (for example customer_name), so they work in any case. Qualify every column with a short table alias in queries that join tables; it makes wrong references obvious when you read the query.

When code has to support several Oracle releases, remember that newer conveniences (such as GROUP BY alias from Release 23) are not available on 19c. Test queries against the oldest release you support.

Common questions

How do I fix ORA-00904 invalid identifier?

Compare the name in the message with ALL_TAB_COLUMNS for the table. Fix the spelling or the table alias, stop using a SELECT alias in WHERE, or use the exact quoted name if the column was created in double quotes.

The column exists, so why is it an invalid identifier?

Most often it was created with a quoted lower-case or mixed-case name, so an unquoted reference looks for the upper-case name. It can also be out of scope, for example a column of an inline view referenced with the inner table alias.

Can I use a column alias in WHERE in Oracle?

No. Oracle allows a SELECT list alias in ORDER BY, and from Release 23 in GROUP BY, but not in WHERE. Repeat the expression or wrap the query in an inline view and filter outside it.

Learn more on CodeWithSQL

Official documentation

Last technically reviewed 24 October 2026; examples and diagnostics run on Oracle Database Error Messages reference (26ai, 21c and 19c); not run locally. Messages and explanations follow Oracle's Database Error Messages reference; no Oracle Database was available, so the examples were not run locally.

Still stuck on an Oracle error?

Browse the full Oracle error library, or get hands-on help from an experienced SQL professional.