select open change scope Open full search

PG.CENTER connects PostgreSQL documentation, reference, and ecosystem knowledge. Maintained by Pigsty.

SQLSTATE / CLASS XX · INTERNAL ERROR

internal_error

SQLSTATE
XX000
Condition name
internal_error
Class
Internal Error
Source macro
ERRCODE_INTERNAL_ERROR
Evidence
Source path confirmed
Analysis & operational context

English SQLSTATE atlas: authored explanations and source/runtime evidence are separate from the manual definitions. View source ↗

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.

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.

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

Source evidence

Evidence belongs to the frozen source and runtime versions listed here. It is not a runtime verification of the selected manual version.

XX000 is the internal_error condition in Class XX, Internal Error, whose source comment covers can't-happen conditions and software bugs.

Method: Read the Class XX section, comment, and XX000 row in the frozen errcodes.txt snapshot.

Limits: The generic class identity does not prove corruption or identify the subsystem.

src.errcodes.18.6

Without a later explicit code, an ERROR or higher level is initialized with ERRCODE_INTERNAL_ERROR, while WARNING starts with ERRCODE_WARNING and lower levels with ERRCODE_SUCCESSFUL_COMPLETION.

Method: Read the default sqlerrcode selection in errstart() in the fixed source.

Limits: Later errcode() construction can replace the initial value; the mapping is not a diagnosis of corruption.

src.elog.18.6

The fixed 18.6 source contains explicit XX000 paths for btree tuple rechecking, invalid dynamic shared-memory control state, configuration parameter redefinition, and strong-random UUID generation failure.

Method: Read each errcode(ERRCODE_INTERNAL_ERROR) branch and its surrounding invariant or service diagnostic.

Limits: These are distinct source paths with different preconditions and severities; they do not establish that a generic XX000 occurrence came from any one of them.

src.nbtinsert.18.6 src.dsm.18.6 src.guc.18.6 src.uuid.18.6

A default elog(ERROR, ...) path such as the invalid-page check starts with XX000 through the generic severity mapping when it supplies no explicit SQLSTATE.

Method: Read the bufmask elog call and the error-stack default mapping.

Limits: The source example is a diagnostic invariant check; a real occurrence requires the exact runtime path and environment.

src.bufmask.18.6 src.elog.18.6

An explicit XX000 does not force one wire severity: the shared-memory path is FATAL, several paths are ERROR, and the access-control hook can carry XX000 at NOTICE.

Method: Read each ereport level alongside its explicit errcode and compare the protocol severity contract.

Limits: The OAT hook is test-module code and does not represent normal production internal-error behavior.

src.dsm.18.6 src.nbtinsert.18.6 src.guc.18.6 src.uuid.18.6 src.oat-hooks.18.6

The protocol distinguishes ERROR/FATAL/PANIC severities in error messages from WARNING/NOTICE/DEBUG/INFO/LOG severities in notice messages, with localized S and nonlocalized V fields.

Method: Read the official PostgreSQL 18 Error and Notice Message Fields table.

Limits: Severity is a wire property and must be interpreted with transaction and connection state.

doc.protocol.18

XX000 alone is insufficient to diagnose data or index corruption; the exact message, source path, severity, and surrounding evidence are required.

Method: Compare the broad Class XX comment and the distinct source paths and messages.

Limits: This is an evidence boundary, not a claim that corruption is impossible when XX000 occurs.

src.errcodes.18.6 src.nbtinsert.18.6 src.bufmask.18.6 src.dsm.18.6

The internal_error_safety_boundary case is recorded as not_applicable on PostgreSQL 18.6 and 10.21 because no safe deterministic SQL trigger was used.

Method: Read the stable case contract and both target summaries/raw records, retaining source_confirmed_only and not_applicable.

Limits: This is not runtime proof of an XX000 occurrence; it records that fault or corruption injection was outside the accepted test boundary.

case-manifest.XX000 runtime-raw.XX000.latest runtime.XX000-debug-latest.latest runtime.XX000-debug-pg10.pg10

The locked catalogue records XX000 in the 7.4–8.4.22 pre-9.0 formal sources, every listed formal snapshot from 9.0.23 through 18.6, and 19beta3. The same-tag REL8_1_4 errcodes.sgml row already lists XX000 as internal_error, confirming that condition-name observation by 8.1.4. The class title changes between the 9.0 header and 9.1 text definitions; candidate source gaps remain for 7.0–7.3. These are presence boundaries, not exact implementation introduction dates.

Method: Read the manifest snapshot and the same-tag REL8_1_4 errcodes.sgml row for the code; the generated per-code record is derived from those entries.

Limits: The pre-9.0 source set is locked through 8.4.22 but candidate source gaps remain for 7.0–7.3; the observed condition-name and class-title changes are not exact implementation introduction dates.

manifest.XX000 doc.errcodes.8.1.4

Message templates

explicit ERROR · message.index-refind

Primary

failed to re-find tuple within index "%s"

Hint

This may be because of a non-immutable index expression.

The message belongs to the UNIQUE_CHECK_EXISTING btree path and carries a relation/constraint context.

explicit FATAL · message.dsm-fatal

Primary

dynamic shared memory control segment is not valid

This path terminates the backend session; it is not a generic client-side error template.

explicit ERROR · message.guc

Primary

attempt to redefine parameter "%s"

The template belongs to the custom GUC placeholder invariant path.

explicit ERROR · message.uuid

Primary

could not generate random values

The message reports failure of the strong-random provider in the UUID path.

default elog(ERROR) · message.bufmask-default

Primary

invalid page pd_lower %u pd_upper %u pd_special %u

The XX000 code is supplied by the default mapping because this elog call has no explicit errcode.

explicit NOTICE · message.oat-notice

Primary

in %s: %s %s %s [%s]

This is a test-module hook diagnostic demonstrating severity/code separation, not a production internal-error occurrence.

Reproduction & repair cases

internal_error_safety_boundary · PG 10, 18

Preconditions

  • A source-confirmed internal error path

Trigger: No deterministic safe SQL trigger is used; forcing internal corruption would violate the disposable-case safety boundary.

Expected assertions

  • The directory and source paths are recorded
  • The batch marks the natural runtime trigger not applicable rather than fabricating one

Repair: Preserve diagnostics, stop treating the session as healthy when appropriate, and investigate the exact server path with supportable evidence.

Cleanup: No runtime resources are created.

Recorded runtime evidence

18.6 (Homebrew) · not_applicable

Run: XX000-debug-latest

No internal-error path was executed.

{
  "id": "runtime.XX000-debug-latest.latest",
  "raw": "verify/results/XX000-debug-latest/latest/raw.jsonl",
  "cases": [
    "internal_error_safety_boundary"
  ],
  "limits": "No internal-error path was executed.",
  "reason": "source_confirmed_only",
  "run_id": "XX000-debug-latest",
  "status": "not_applicable",
  "target": "latest",
  "summary": "verify/results/XX000-debug-latest/latest/summary.json",
  "server_version": "18.6 (Homebrew)",
  "server_version_num": 180006
}
10.21 (Debian 10.21-1.pgdg90+1) · not_applicable

Run: XX000-debug-pg10

No internal-error path was executed.

{
  "id": "runtime.XX000-debug-pg10.pg10",
  "raw": "verify/results/XX000-debug-pg10/pg10/raw.jsonl",
  "cases": [
    "internal_error_safety_boundary"
  ],
  "image": "postgres@sha256:b2baf8998630663d21370da06387c950e587071bdd307ee34e661cdcc7442bcc",
  "limits": "No internal-error path was executed.",
  "reason": "source_confirmed_only",
  "run_id": "XX000-debug-pg10",
  "status": "not_applicable",
  "target": "pg10",
  "summary": "verify/results/XX000-debug-pg10/pg10/summary.json",
  "server_version": "10.21 (Debian 10.21-1.pgdg90+1)",
  "server_version_num": 100021
}

Definition snapshot: english-manuals:7f4a099273d70467b2a86881516… · English manual source