When PostgreSQL raises SQLSTATE XX000, the error message typically looks like:
ERROR: XX000: could not <action>: <reason>
or more commonly, a message that includes the phrase "internal error" or describes an unexpected server-side condition, for example:
ERROR: XX000: unexpected chunk number 0 (expected 1) for toast value 12345 in pg_toast_2619
The condition name is internal_error and it belongs to SQLSTATE class XX — the "Internal Error" class. This means PostgreSQL encountered a condition it considers a bug or an invariant violation inside its own code.
What This Error Means
SQLSTATE class XX covers errors that represent bugs or unexpected internal states within the PostgreSQL server itself, as opposed to errors caused by user input or schema violations. The class contains three conditions: XX000 (internal_error), XX001 (data_corrupted), and XX002 (index_corrupted). An XX000 is the catch-all: something went wrong inside the server that does not fit a more specific category.
When PostgreSQL raises XX000, it means the database server hit a code path that should not have been reached, or encountered an internal state that its own checks deemed impossible. This is distinct from user-facing errors like constraint violations or type mismatches — those are expected and handled gracefully. An internal_error signals that PostgreSQL's own assumptions were violated.
The transaction that triggers this error is aborted immediately. Depending on where in the execution the error surfaced, the backend process may also terminate, producing a corresponding entry in the PostgreSQL server log (postgresql.log). The rest of the cluster is unaffected; other connections continue normally.
Common Causes
A known PostgreSQL bug. Some
XX000errors reproduce reliably on a specific minor version and are fixed in a later patch release. Checking the PostgreSQL mailing lists and release notes for your exact version and error message is often the fastest way to confirm this.TOAST corruption. Large field values are stored out-of-line in TOAST tables. If a TOAST tuple is missing, truncated, or has an unexpected chunk sequence, PostgreSQL raises
XX000with a message referencingpg_toast_<oid>. This is typically caused by a prior crash without a clean shutdown, hardware-level storage errors, or a filesystem that does not honorfsync.Catalog or index inconsistency. If a system catalog row is in an inconsistent state — for example after a crash mid-DDL — subsequent queries that read that catalog entry may trip an internal assertion and raise
XX000.Custom C extensions or procedural languages. A poorly written C extension,
pg_cronjob, or PL/Python/PL/Perl function that corrupts memory or returns an unexpected type can cause the backend to raiseXX000before it detects a more specific fault.Hardware or OS-level storage errors. Silent data corruption from a faulty disk, RAID controller, or memory issue can cause PostgreSQL to read unexpected bytes and fail internal consistency checks, surfacing as
XX000before the kernel has reported any I/O error.
How to Fix internal_error
Capture the full server log entry. The
ERRORline in the client is often abbreviated. The PostgreSQL server log (log_min_messages = erroror lower) includes the file name, line number, and aDETAILorHINTthat pinpoints the internal code path. Collect this before doing anything else.# Tail the PostgreSQL log (path varies by installation) tail -n 100 /var/log/postgresql/postgresql-*.log | grep -A 10 "XX000"Check the PostgreSQL version and release notes. If the log points to a specific internal function, search the PostgreSQL commit log and the pgsql-bugs mailing list for that function name and your version.
SELECT version();Upgrading to the latest patch release (
15.x,16.x, etc.) resolves manyXX000errors that were genuine bugs.Run
VACUUM FULLand check for TOAST corruption. If the error message names apg_toast_table, verify and repair it:-- Identify the table whose TOAST table has OID matching pg_toast_<oid> SELECT relname FROM pg_class WHERE reltoastrelid = '<toast_oid>'::oid; -- Run a full vacuum to rebuild TOAST storage VACUUM FULL VERBOSE <table_name>;If VACUUM fails with the same error, the data in that row may be unrecoverable without a backup restore.
Use
pg_dumpto test which row is corrupt. If TOAST corruption is suspected, a selective dump can isolate the offending row:pg_dump -t <table_name> --no-privileges mydb > /dev/nullErrors during dump will identify the corrupt row. Use
DELETEto remove it if the data is expendable, or restore it from backup.Run
pg_amcheck(PostgreSQL 14+) orCHECK TABLEequivalents. Thepg_amcheckutility verifies heap and index structural consistency without acquiring heavy locks:pg_amcheck --verbose -d mydbCheck hardware and filesystem integrity. If multiple unrelated
XX000errors appear across tables, investigate storage health (smartctl, RAID controller logs) and ensurefsyncis enabled and working. Never disablefsyncin production.Report the bug. If you cannot reproduce it consistently or it occurs on a fully up-to-date PostgreSQL installation, file a report at https://www.postgresql.org/account/submitbug/. Include the full server log entry, PostgreSQL version (
SELECT version()), OS, and steps to reproduce.
Additional Information
- SQLSTATE class
XXis reserved for internal PostgreSQL errors. The sibling codes areXX001(data_corrupted) andXX002(index_corrupted). The latter two are raised by more specific integrity checks, such aspg_amcheckfindings;XX000is the general-purpose internal failure code. - Most PostgreSQL client drivers (libpq, psycopg2, asyncpg, JDBC, node-postgres) map
XX000to a generic server error exception. In Python/psycopg2 it surfaces aspsycopg2.errors.InternalError_; in JDBC asPSQLExceptionwith SQLStateXX000. - Logical replication subscribers and
pg_logicalslots can occasionally emitXX000when decoding a WAL record that was written by a now-fixed bug on the primary. Upgrading both primary and replica to the same minor version resolves this. XX000errors do not appear inpg_stat_activityafter the fact — they are only visible in the server log at the time they occur. Enablelog_min_messages = warning(or lower) andlog_error_verbosity = verboseinpostgresql.confto ensure the full context is captured.
Frequently Asked Questions
Is SQLSTATE XX000 always a PostgreSQL bug?
Almost always, yes. Class XX errors are explicitly designated in the PostgreSQL source as conditions that "should not happen." If you encounter one, it is either a known bug in your current minor version, a hardware/storage integrity problem, or, less commonly, a bug introduced by a C extension. It is not caused by a SQL mistake or schema design issue.
Will restarting PostgreSQL fix it? Possibly, if the error was caused by a transient in-memory state inconsistency. However, if the cause is TOAST or catalog corruption on disk, or an OS/storage problem, a restart will not help and the error will recur. Investigate before restarting a production server.
My error message says "XX000: could not read block N in file..." — is that still internal_error?
Messages like "could not read block" with XX000 indicate a storage-level read failure. This points to a hardware issue, filesystem problem, or missing data file rather than a code bug. Check disk health and filesystem integrity first.
How do I find which row is causing the XX000 error?
Enable log_error_verbosity = verbose in postgresql.conf and re-run the failing query. The server log will include a CONTEXT line with the internal function and, often, the OID or ctid of the problematic row. You can then look up the row with SELECT ctid, * FROM <table> WHERE ctid = '(<block>,<offset>)'::tid.