# 44000 — with_check_option_violation

> PostgreSQL SQLSTATE 44000: view WITH CHECK OPTION diagnostics and repair.
---

# 44000 — with_check_option_violation

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

`44000` means that a row written through a view would not satisfy that view's `WITH CHECK OPTION` predicate. It protects the view's write invariant; it is not the table-level `CHECK` condition `23514`.

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

| Field | Value |
| --- | --- |
| SQLSTATE | `44000` |
| Condition | `with_check_option_violation` |
| 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_WITH_CHECK_OPTION_VIOLATION` |
| Aliases | `—` |

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

## Meaning {#meaning}

When an automatically updatable view is defined with `WITH CHECK OPTION`, PostgreSQL checks that an inserted or updated row remains visible through the view. The executor treats both `FALSE` and `NULL` from the view predicate as failure, so a nullable predicate column does not make an unknown row pass. `ExecWithCheckOptions` reports `new row violates check option for view "%s"`; the dynamic failing-row DETAIL is included only when the permissions allow the row description. `LOCAL` checks conditions defined directly in the current view; underlying base-view conditions are not checked unless those base views also specify `CHECK OPTION`. `CASCADED` checks the current view and all underlying base-view conditions. `CHECK OPTION` is supported only on automatically updatable views without an `INSTEAD OF` trigger or rule; a trigger-updatable base view and an `INSTEAD` rewrite are separate boundaries where cascading or all checks can be ignored. The selected natural case is a directly updatable view.

## Diagnosis {#diagnosis}

Capture SQLSTATE, view name, DETAIL when present, and the exact row values sent through the view. Read the definition with `pg_get_viewdef()` and evaluate its predicate using SQL three-valued logic: only `TRUE` is visible through the check option, while `FALSE` and `NULL` fail. Check whether the view is automatically updatable, whether it uses `LOCAL` or `CASCADED`, and whether a base view has an `INSTEAD OF` trigger or an `INSTEAD` rewrite. Do not search only for table CHECK constraints: `LOCAL` does not check ordinary underlying-view predicates, while `CASCADED` does unless a trigger-updatable or rewritten boundary prevents that cascade. Missing DETAIL can be a permission boundary rather than proof that no row was checked.

## Response {#response}

Correct the row so its predicate is `TRUE`, or change the view definition only after confirming the schema contract and the intended `LOCAL`/`CASCADED` scope. Keep the check option when the view is meant to be an enforced filtered interface. If a trigger-updatable base view or an `INSTEAD` rewrite is involved, inspect that boundary and its effective checks; do not assume a cascaded check reached it. After an autocommit error the selected connection remained `IDLE`; inside an explicit transaction rollback the failed block before retrying with a valid row.

## Messages {#messages}

The fixed source templates are `new row violates check option for view "%s"` and `Failing row contains %s.`. The view identifier and row rendering are dynamic. Treat DETAIL as diagnostic data and avoid copying it into a stable parser without accounting for values and formatting.

## Representative case {#case}

The shared registry in `verify/cases/44000/snippets.json` (SHA-256 `7b227bca904c860388c6cf1f5b7f551412b4d67832fb91aaa682f4126072e41c`) creates a filtered view, attempts one row outside the predicate, and then inserts a valid row. See [the public case export](../data/cases/44000.json) and [structured evidence](../data/evidence/44000.json).

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

```sql
CREATE TABLE items(id integer PRIMARY KEY, visible boolean NOT NULL, note text NOT NULL);
CREATE VIEW visible_items AS SELECT id, visible, note FROM items WHERE visible WITH CHECK OPTION;
INSERT INTO visible_items VALUES (1, false, 'hidden');
INSERT INTO visible_items VALUES (1, true, 'visible');
SELECT id, visible, note FROM visible_items ORDER BY id;
```
<!-- END SQLSTATE SNIPPET -->

The runner qualifies registry relation names inside a private schema. It asserts the natural server SQLSTATE and view diagnostic, then verifies that only the visible row was accepted.

The selected case observed `44000` on PostgreSQL 18.6 and 10.21 when a row with `visible = false` was inserted through a view with `WITH CHECK OPTION`. The DETAIL contained the failing row, autocommit stayed `IDLE`, and a row satisfying the predicate was accepted.

## Versions {#versions}

The locked catalogue records this condition from the early historical boundary through the formal snapshots. The fixed source has the same mechanism in 18.6 and 10.23; the selected runtime case covers a simple insert through a view on 18.6 and 10.21, not every view-rule composition.

## Related {#related}

Compare [23514 check violation](../23514/) for a table CHECK and [27000 triggered data change violation](../27000/) for trigger-driven same-command changes.

## Sources {#sources}

- [`src.errcodes.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt) (SHA-256 `6e8de346643ba84aa3c9c6a73360acfc7b2dfb89162c06c08ce9bf5bcd5bbcba`)
- [`src.execMain.18.6`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/executor/execMain.c#L2322-L2327) (SHA-256 `33b97337fa23a649c5e7a092e1bd405a54e8503529236c62c9d5bb93a1774a8d`)
- [`CREATE VIEW documentation`](https://www.postgresql.org/docs/18/sql-createview.html) · local call scan `src.calls.REL_18_6` (SHA-256 `9ee8a0e81d8f0825c5c1ae45583439859a26e602bdd4ce2f2a62aa278867ccbf`)
