PostgreSQL Wrong Object Type (SQLSTATE 42809)

PostgreSQL raises ERROR: <name> is not a table (or similar) with SQLSTATE 42809 and condition name wrong_object_type when a DDL or DML command targets an object of an incompatible type. For example, you'll see this error if you attempt ALTER TABLE on a view, VACUUM a sequence, or use TRUNCATE on a foreign table that does not support it.

What This Error Means

SQLSTATE 42809 belongs to error class 42 — "Syntax Error or Access Rule Violation." Despite the class name, wrong_object_type is not a syntax error; it means the SQL is syntactically valid but semantically incorrect because the named object exists yet is of the wrong kind for the requested operation.

PostgreSQL's catalog stores all database objects (tables, views, sequences, materialized views, foreign tables, indexes, composite types, etc.) in pg_class and related system catalogs. When you execute a command, PostgreSQL looks up the object by name, finds it, and then checks whether its relkind (or equivalent attribute) matches what the command requires. If the kinds do not match, it raises 42809 before any data access occurs.

The transaction is not automatically aborted by this error — the connection remains open — but any ongoing explicit transaction will be in an aborted state and must be rolled back before further work can proceed. Because the command never executes, no data is modified.

Common Causes

  1. Applying a table-specific DDL command to a view. Commands such as ALTER TABLE ... ADD COLUMN, CLUSTER, or TRUNCATE do not apply to views. A view has relkind = 'v', not 'r' (ordinary table).

  2. Using VACUUM or ANALYZE on a non-table object. While ANALYZE accepts views in some contexts, VACUUM requires a plain table or materialized view. Attempting VACUUM on a sequence or composite type raises this error.

  3. Running ALTER SEQUENCE on a table or view. If a developer mistypes the object name and happens to match a table rather than a sequence, PostgreSQL raises 42809 rather than 42P01 (undefined_table).

  4. Calling REINDEX TABLE or CLUSTER on a materialized view or foreign table when the operation requires a specific kind of relation.

  5. ORM or migration tool confusion. Code-generation tools or migration frameworks sometimes detect object names without checking relkind, then emit the wrong DDL variant (e.g., issuing ALTER TABLE on an object that was manually replaced with a view).

How to Fix wrong_object_type

  1. Verify the object kind before issuing the command. Query pg_class to confirm what you are targeting:

    SELECT relname, relkind
    FROM pg_class
    WHERE relname = 'your_object_name'
      AND relnamespace = 'your_schema'::regnamespace;
    

    Common relkind values: r = ordinary table, v = view, m = materialized view, S = sequence, f = foreign table, i = index, c = composite type.

  2. Use the correct command for the object type. If the object is a view and you want to add a column, you must redefine the view with CREATE OR REPLACE VIEW or drop and recreate it. You cannot ALTER TABLE a view.

    -- Wrong: raises 42809 if my_view is a view
    ALTER TABLE my_view ADD COLUMN new_col integer;
    
    -- Correct: recreate the view with the additional expression
    CREATE OR REPLACE VIEW my_view AS
    SELECT id, name, 0 AS new_col
    FROM base_table;
    
  3. Use ALTER MATERIALIZED VIEW instead of ALTER TABLE for materialized views. PostgreSQL provides separate DDL commands for materialized views:

    -- Wrong
    ALTER TABLE my_matview SET (autovacuum_enabled = true);
    
    -- Correct
    ALTER MATERIALIZED VIEW my_matview SET (autovacuum_enabled = true);
    
  4. Use ALTER SEQUENCE for sequences, not ALTER TABLE:

    -- Correct
    ALTER SEQUENCE my_seq INCREMENT BY 5;
    
  5. Fix migration scripts that conflate object types. If an ORM emits ALTER TABLE for something that is a view in your database (perhaps a table that was later replaced by a view), you need to either restore the underlying table or update the migration to use view-compatible DDL.

Additional Information

  • SQLSTATE 42809 has been part of PostgreSQL's error catalog since at least PostgreSQL 8.x; the behavior is unchanged in modern versions (PostgreSQL 14–17).
  • Related SQLSTATE codes in class 42:
    • 42P01 (undefined_table) — object does not exist at all
    • 42710 (duplicate_object) — object of that name already exists
    • 42501 (insufficient_privilege) — object exists and is the right type, but access is denied
    • 42601 (syntax_error) — syntactically malformed statement
  • psycopg2 / psycopg3: raises psycopg2.errors.WrongObjectType (subclass of ProgrammingError). The SQLSTATE can be read from e.pgcode.
  • JDBC: surfaces as org.postgresql.util.PSQLException with getSQLState() returning "42809".
  • SQLAlchemy / Alembic: ORM auto-reflect or op.alter_column() can trigger this if the target object was manually replaced with a view between migrations. Add an explicit relkind check in the migration's upgrade() function when in doubt.
  • This error has no performance implications — no data access occurs before it is raised.

Frequently Asked Questions

Why does PostgreSQL say "is not a table" even though the object clearly exists? PostgreSQL found the object in the catalog but determined its relkind is not 'r' (ordinary table). The object exists — it is just a different kind (view, sequence, materialized view, etc.). Use the pg_class query above to confirm what kind it is.

Can I use ALTER TABLE on a materialized view? No. Materialized views have their own DDL command: ALTER MATERIALIZED VIEW. Some storage parameters can be set on materialized views this way, but column-level changes require dropping and recreating the materialized view.

My migration tool generated ALTER TABLE for an object that used to be a table but is now a view. What should I do? You have two options: revert the view back to a table (if that is safe), or rewrite the migration step to use CREATE OR REPLACE VIEW instead of ALTER TABLE. The right choice depends on whether the view was intentional and whether dependent objects exist.

Does this error abort my transaction? The error itself does not roll back the transaction, but PostgreSQL puts the transaction into an error state. You must issue ROLLBACK (or ROLLBACK TO SAVEPOINT if you used a savepoint) before sending any further commands on that connection. In autocommit mode, there is no explicit transaction to roll back.

Subscribe to the Pulse Newsletter

Get early access to new Pulse features, insightful blogs & exclusive events , webinars, and workshops.

We use cookies to provide an optimized user experience and understand our traffic. To learn more, read our use of cookies; otherwise, please choose 'Accept Cookies' to continue using our website.