# XX000 — internal_error

> PostgreSQL uses SQLSTATE XX000 for internal-error paths and as the default code for an ERROR without an explicit code. Preserve the full diagnostic and investigate the exact subsystem; XX000 alone does not prove corruption.
---

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

`XX000` is the `internal_error` condition in Class XX, `Internal Error`. The PostgreSQL source directory describes this class as covering “can't-happen” conditions and software bugs. It is a generic diagnostic boundary, not a claim that a database is corrupt.

The code also has a mechanical default. In the error stack initializer, an `ERROR` or higher level with no later explicit code starts as `ERRCODE_INTERNAL_ERROR`; `WARNING` starts as `ERRCODE_WARNING` (`01000`); and a level below `WARNING` starts with `00000` unless it supplies its own code. An explicit `errcode()` can select another SQLSTATE. Therefore, `XX000` can arise from a default `elog(ERROR, ...)` path or from a deliberate internal-error report.

Severity and connection consequences are path-dependent. `ERROR`, `FATAL`, and `PANIC` are different protocol severities; an explicit `XX000` can even accompany a `NOTICE` in extension test code. Read the severity, transaction status, connection state, source location, and complete diagnostic together.

The representative case `internal_error_safety_boundary` is source-only. It is recorded as `not_applicable` on PostgreSQL 18.6 and 10.21 because forcing internal corruption or a fault would cross the safe disposable SQL boundary. No fabricated `RAISE` is presented as a natural internal server failure.

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

| Field | Value |
| --- | --- |
| SQLSTATE | `XX000` |
| Condition | `internal_error` |
| 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_INTERNAL_ERROR` |
| Aliases | `—` |

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

## Meaning and trigger paths {#meaning}

The 18.6 definition is `XX000 E ERRCODE_INTERNAL_ERROR internal_error` under Class XX. The class comment is intentionally broad. More specific codes exist for data corruption (`XX001`) and index corruption (`XX002`); choosing `XX000` does not silently upgrade a generic internal report into either diagnosis.

There are two source patterns to distinguish:

1. **Default internal code.** A backend path calls `elog(ERROR, ...)` or otherwise reaches `ereport(ERROR, ...)` without an explicit SQLSTATE. `elog.c` initializes that error as `XX000`. The message and source function still identify the subsystem.
2. **Explicit internal code.** A path calls `errcode(ERRCODE_INTERNAL_ERROR)` because an invariant or an operating-system/service result was considered impossible or unusable. In 18.6, examples include btree rechecking that cannot re-find a tuple, an invalid dynamic shared-memory control segment, an attempt to redefine a non-placeholder configuration parameter, and failure to obtain strong random bytes for UUID generation.

The explicit code does not force one severity. The shared-memory path reports `FATAL`, while the btree, GUC, and UUID examples report `ERROR`. A test access-control hook intentionally reports `NOTICE` with `ERRCODE_INTERNAL_ERROR`, showing that SQLSTATE and severity are separate fields. That hook is a source example, not evidence that ordinary production notices indicate an internal failure.

## Messages and diagnostics {#messages}

`XX000` has no universal primary message. Representative source templates include:

- `failed to re-find tuple within index "%s"`, with a hint that a non-immutable index expression may be responsible (`nbtinsert.c`).
- `dynamic shared memory control segment is not valid` on the `FATAL` initialization path (`dsm.c`).
- `attempt to redefine parameter "%s"` (`guc.c`) and `could not generate random values` (`uuid.c`).
- `invalid page pd_lower %u pd_upper %u pd_special %u` from a default `elog(ERROR, ...)` path (`bufmask.c`); the default mapping supplies `XX000` when no explicit code replaces it.

Treat these as path-specific diagnostic templates. Record SQLSTATE, both severity fields, primary message, detail, hint, context, source file, function, line, backend PID, database, and server version. Localized text and template wording can vary; the source path and structured fields are more stable than a message substring.

## Diagnosis {#diagnosis}

Preserve the complete client error and the matching server log record before retrying or restarting anything. First classify the protocol severity:

- An `ERROR` normally aborts the current explicit transaction but leaves the backend connection available after rollback.
- A `FATAL` ends the backend session; the client must establish a new connection before continuing.
- A `PANIC` is a server-wide emergency path whose process and connection effects must be confirmed from the server log and supervision state.
- A `WARNING` or `NOTICE` with an explicit `XX000` has different control flow and does not by itself prove an internal failure or abort a transaction.

Then group the event by source function and subsystem. Check for a preceding I/O, memory, extension, index, configuration, or concurrency failure; compare the exact server build and fixed source revision; and look for repeated occurrences. Use read-only catalog, index, or page checks only when the diagnostic points to them. Do not infer corruption from the class name alone, and do not replace the message with a hand-written `RAISE EXCEPTION` test: that would exercise `P0001` by default rather than the server's internal path.

## Response {#response}

Follow the severity and the subsystem. Roll back an `ERROR` transaction before issuing unrelated commands; reconnect after `FATAL`; and follow the server's restart and incident procedure after `PANIC`. Do not blindly retry an internal-error operation, especially when the source points to an invariant, storage, index, or shared-memory boundary.

Collect the server version, exact SQL, backend and server logs, source location, relation or object identity, and recent configuration or extension changes. If the evidence points to corruption, stop writes as appropriate for the incident, preserve a copy or snapshot, and use the documented PostgreSQL recovery and support process. If it points to a deterministic software or extension defect without corruption evidence, isolate the reproducer and compare supported versions. The correct response is determined by that evidence, not by `XX000` alone.

## Versions {#versions}

The catalogue records `XX000` in the locked 7.4–8.4.22 pre-9.0 formal sources, every formal snapshot from 9.0.23 through 18.6, and the 19 Beta 3 preview. The same-tag `REL8_1_4` `errcodes.sgml` table already lists `XX000` as `internal_error`, confirming that condition-name observation by 8.1.4. The class title is `Internal Error (PostgreSQL-specific error class)` in the 9.0 header view and changes to `Internal Error` in the 9.1 text definition. Candidate source gaps remain for 7.0–7.3; these are observed catalogue boundaries, not exact implementation introduction dates.

The fixed 18.6 source commit is `724edf9bde9d356724ad384a2e196edc3c9f80f7`. The default severity-to-code mapping and the source paths above are 18.6 evidence. No deterministic safe SQL trigger was accepted for the representative runtime case, so the runtime status remains `not_applicable` on both targets.

## Related {#related}

[`XX001` — `data_corrupted`](../xx001/) and [`XX002` — `index_corrupted`](../xx002/) are more specific corruption conditions. [`P0001` — `raise_exception`](../p0001/) is the normal default for an explicit PL/pgSQL `RAISE EXCEPTION` without a code. [`57P01` — `admin_shutdown`](../57p01/) is a server-connection event with a different class and response. [`25P02` — `in_failed_sql_transaction`](../25p02/) is a follow-on transaction state after an earlier error, not a synonym for `XX000`.

## Sources {#sources}

The structured evidence is recorded in the [public evidence JSON](../data/evidence/xx000.json). Source records are pinned to PostgreSQL commit `724edf9bde9d356724ad384a2e196edc3c9f80f7`; runtime records retain the exact source-only case ID and both target summaries.

- `src.errcodes.18.6` — [`errcodes.txt`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/errcodes.txt#L496-L502)
- `src.elog.18.6` — [`elog.c`](https://github.com/postgres/postgres/blob/724edf9bde9d356724ad384a2e196edc3c9f80f7/src/backend/utils/error/elog.c#L442-L454)
- `src.nbtinsert.18.6`, `src.dsm.18.6`, `src.guc.18.6`, `src.uuid.18.6`, and `src.bufmask.18.6` — source-confirmed internal paths
- `src.oat-hooks.18.6` — explicit internal code at `NOTICE`
- `doc.protocol.18` — [Error and Notice Message Fields](https://www.postgresql.org/docs/18/protocol-error-fields.html)
