Skip to content

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

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.

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

Meaning and trigger paths

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

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

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

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

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.

XX001data_corrupted and XX002index_corrupted are more specific corruption conditions. P0001raise_exception is the normal default for an explicit PL/pgSQL RAISE EXCEPTION without a code. 57P01admin_shutdown is a server-connection event with a different class and response. 25P02in_failed_sql_transaction is a follow-on transaction state after an earlier error, not a synonym for XX000.

Sources

The structured evidence is recorded in the public evidence 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.6errcodes.txt
  • src.elog.18.6elog.c
  • 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.18Error and Notice Message Fields