# 42809 — Wrong object type

> PostgreSQL SQLSTATE 42809: Wrong object type (wrong_object_type), source-backed diagnosis and recovery guidance.
---

# 42809 — Wrong object type

## At a glance {#at-a-glance}

`42809` is **wrong_object_type**: a command is valid in general but not for the selected object kind. The case tries a row-level BEFORE trigger on a view.

<!-- BEGIN SQLSTATE FACTS: generated by scripts/generate.py; do not edit -->

| Field | Value |
| --- | --- |
| SQLSTATE | `42809` |
| Condition | `wrong_object_type` |
| Status | `active` |
| Known present by | `7.4` |
| Locked snapshots | `9.0.23, 9.1.24, 9.2.24, 9.3.25, 9.4.26, 9.5.25, 9.6.24, 10.23, 11.22, 12.22, 13.23, 14.24, 15.19, 16.15, 17.11, 18.6, 19beta3` |
| Macros | `ERRCODE_WRONG_OBJECT_TYPE` |
| Aliases | `—` |

<!-- source facts: data/errcodes/42809.json -->
<!-- END SQLSTATE FACTS -->

## Meaning {#meaning}

The view-specific guard reports `"%s" is a view`, with DETAIL `Views cannot have row-level BEFORE or AFTER triggers.` Inspect `pg_class.relkind`, event, level, and timing. This is distinct from a missing relation and from a trigger-body error; the selected autocommit connection is `IDLE` after the failed definition.

## Diagnosis {#diagnosis}

Inspect pg_class.relkind and the trigger event, level, and timing. Confirm whether the target is a view or table; a trigger-body error is a later stage and has a different diagnostic.

## Response {#response}

Use the trigger form supported by the object: an `INSTEAD OF` row trigger on the view. If the business rule needs BEFORE/AFTER row timing, put it on a base table and keep view write semantics explicit. The selected run checks trigger definition success, not actual view DML behavior. In an explicit transaction, the rejected trigger definition leaves `INERROR`; roll back or return to a suitable savepoint before retrying. The selected autocommit path returns to `IDLE`.

## Observed diagnostics {#messages}

The source group is explicit `ERROR` with primary `"%s" is a view` and detail `Views cannot have row-level BEFORE or AFTER triggers.`

## Representative case {#case}

The runner creates a view and function, attempts the forbidden trigger, then creates an INSTEAD OF trigger and verifies its catalog row.

<!-- BEGIN SQLSTATE SNIPPET: view_row_trigger_type -->

```sql
CREATE VIEW syntax_schema.trigger_view AS SELECT 1 AS value;
CREATE FUNCTION syntax_schema.trigger_function() RETURNS trigger LANGUAGE plpgsql AS 'BEGIN RETURN NEW; END';
CREATE TRIGGER row_trigger BEFORE INSERT ON syntax_schema.trigger_view FOR EACH ROW EXECUTE PROCEDURE syntax_schema.trigger_function();
CREATE TRIGGER instead_trigger INSTEAD OF INSERT ON syntax_schema.trigger_view FOR EACH ROW EXECUTE PROCEDURE syntax_schema.trigger_function();
SELECT count(*) FROM pg_trigger t JOIN pg_class c ON c.oid = t.tgrelid JOIN pg_namespace n ON n.oid = c.relnamespace WHERE n.nspname = 'syntax_schema_name' AND c.relname = 'trigger_view' AND t.tgname = 'instead_trigger' AND NOT t.tgisinternal;
```

<!-- END SQLSTATE SNIPPET -->

The selected 18.6 and 10.21 runs passed SQLSTATE, severity, state/recovery, repair, cleanup, and isolated-target stop assertions. See [`case JSON`](../data/cases/42809.json) and [`authored evidence`](../data/evidence/42809.json); private manifest and registry hashes are recorded there.

## Versions {#versions}

The locked catalogue contains this condition from the 7.4 presence bound through the listed snapshots. The selected natural case passed on PostgreSQL 18.6 and 10.21; that bounded result does not infer every intermediate release or every source branch.

## Related {#related}

- [`42P01`](../42p01/)
- [`42830`](../42830/)

## Sources {#sources}

- `src.errcodes.REL_18_6` — fixed definition at `724edf9bde9d356724ad384a2e196edc3c9f80f7`; SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba` ([source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt#L1-L1)).
- `src.view-row-trigger.18.6` — `src/backend/commands/trigger.c` lines 274–278 at `724edf9bde9d356724ad384a2e196edc3c9f80f7`; blob SHA-256 `0a539af85b0de1a04779f92202e7bfc77d85ae6da1747ea32527c67f39084fbd` ([source](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/commands/trigger.c#L274-L278)).
- `src.view-row-trigger.10.23` — `src/backend/commands/trigger.c` lines 208–212 at `02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4`; blob SHA-256 `dac7cdcbe2df1a1bbc77daa23e808b0c81ba8891c9895f10e8b270a15316e225` ([source](https://github.com/postgres/postgres/blob/02991e79f8f58bc208f05dcc8af0c62dbe0a6ea4/src/backend/commands/trigger.c#L208-L212)).
- `src.calls.REL_18_6` / `src.calls.REL_10_23` — fixed call scans, SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf` / `00d16d3eb01b71ccf1b245c8f3102f9d0ec9f36fb02777b8dd1b99fcb263040c`.
- `manifest.42809` / `snippet-registry.42809` — hashes are recorded in `evidence/42809.json` and each runtime record.
