When PostgreSQL detects that its own stored data is internally inconsistent or structurally invalid, it raises ERROR: could not read block N in file "base/...": read only 0 of 8192 bytes or similar messages accompanied by SQLSTATE XX001 (data_corrupted). This is an internal server error class — the error originates inside PostgreSQL itself, not from a query logic problem.
What This Error Means
SQLSTATE class XX covers internal errors — conditions that should never occur under normal PostgreSQL operation. XX001 specifically signals that PostgreSQL has found corruption in a data page: the on-disk representation of a heap (table) page, index page, visibility map, or free space map does not match what PostgreSQL expects. Unlike constraint violations or type errors, this error does not indicate a user mistake; it indicates a problem with the data PostgreSQL has written to or is reading from storage.
PostgreSQL stores data in 8 kB pages. Each page carries a checksum (when data_checksums is enabled) and internal structural metadata including page headers, item pointers, and line pointer arrays. When PostgreSQL reads a page and finds the header magic number invalid, the checksum wrong, tuple offsets that point outside the page, or other structural violations, it raises XX001 rather than silently returning bad data.
The transaction that encountered the corruption is immediately aborted. Depending on which relation is corrupt and how it is accessed, the error may surface only for specific queries or may block autovacuum and background workers as well. Importantly, the corruption is in the data files on disk — reconnecting or retrying the query will reproduce the same error.
Common Causes
Storage hardware failure or firmware bug. Disk write errors, bad sectors, failing SSDs, or faulty RAID controllers can cause partial or silent writes. Data written by PostgreSQL is stored incorrectly on disk, and corruption is detected on subsequent reads.
File system or OS-level bugs. Bugs in file system drivers, incorrect use of
O_DIRECT, or a file system that does not guarantee write ordering can cause pages to be written out of order or incompletely during a crash, especially whenfsynchas been disabled.Memory corruption (RAM errors). Faulty RAM or a DIMM with uncorrectable ECC errors can corrupt data in the PostgreSQL shared buffer cache before it is written to disk. The corrupted data is then persisted.
Improper PostgreSQL shutdown or crash without WAL replay. Copying data files while PostgreSQL is running, restoring a backup to a wrong point-in-time, or manually moving or truncating files in the data directory can leave data files in an inconsistent state that WAL replay cannot reconcile.
Checksum mismatch after enabling
data_checksumson an existing cluster. Ifpg_checksumswas run while the cluster was online, or if pages were modified between the offline checksum pass and startup, stale checksums can trigger falseXX001errors.Bugs in PostgreSQL extensions or storage engines that write directly to relation files using low-level APIs can occasionally produce malformed pages that PostgreSQL later rejects.
How to Fix data_corrupted
Identify the corrupted object. The error message almost always includes the file path and block number. Map the file back to a relation using:
SELECT relname, relkind FROM pg_class WHERE relfilenode = <filenode_number>;For example, if the error references
base/16384/12345, the filenode is12345. You can also querypg_relation_filepath('mytable')to confirm.Check PostgreSQL logs for the full error chain. Look at
postgresql.logforDETAILandCONTEXTlines — they often include the block number, offset, and the type of page that is corrupt (heap, index, toast, etc.).Run
pg_dumpto assess scope. Try dumping the affected table:pg_dump -t schema.tablename -Fc mydb > /tmp/tablename_dump.dumpIf
pg_dumpalso errors onXX001, the corruption is confirmed on that table. If it succeeds, the corruption may be limited to a non-critical secondary structure (e.g., an index).For corrupt indexes — reindex. If the affected file is an index, simply drop and rebuild it:
REINDEX TABLE CONCURRENTLY myschema.mytable; -- or for a specific index: REINDEX INDEX CONCURRENTLY myschema.myindex;Index corruption does not imply heap corruption. Rebuilding the index from the heap data is safe if the heap itself is clean.
For corrupt heap pages — use
zero_damaged_pagesas a last resort. If you have no usable backup and can tolerate data loss for the affected pages:-- As a superuser, in a session where you accept data loss: SET zero_damaged_pages = on; SELECT * FROM mytable; -- forces PostgreSQL to zero out corrupt pages instead of erroring RESET zero_damaged_pages; VACUUM FULL mytable;This will permanently destroy data on the corrupt pages. Use only when data loss is acceptable and you have confirmed no backup is available.
Restore from backup. For heap corruption where data integrity matters, a restore from a verified backup (logical dump, pg_basebackup, or a PITR snapshot) is the correct solution. Test the backup before assuming it is clean.
Investigate the underlying hardware. After addressing the immediate data issue, check storage health using
smartctl, kernel logs (dmesg | grep -i error), and file system integrity tools. A recurringXX001without an apparent cause strongly suggests hardware problems.
Additional Information
- SQLSTATE class
XX(Internal Error) contains three codes:XX000(internal_error),XX001(data_corrupted), andXX002(index_corrupted). If the error explicitly names an index,XX002may be raised instead ofXX001. data_checksumsmust be enabled atinitdbtime (or retrofitted withpg_checksumsin PostgreSQL 12+). Without checksums, many forms of silent corruption go undetected until PostgreSQL encounters a structurally invalid page — at which point damage may already be widespread.- PostgreSQL 14+ includes
pg_amcheck, a dedicated corruption-checking utility that can scan heap and index pages for structural problems without requiring an active error to occur:pg_amcheck -d mydb --all. - Drivers and ORMs that wrap all server errors into a generic "database error" may obscure
XX001. In Java (JDBC), theSQLStatefield will be"XX001"; in psycopg2 (Python), checke.pgcode. - Autovacuum and background workers that encounter
XX001will log the error and back off, but they will not crashpostmaster. The affected table will accumulate dead tuples if autovacuum cannot process it.
Frequently Asked Questions
Can I recover data from a corrupt page without a backup?
Sometimes. If the corruption is limited to a few pages, zero_damaged_pages = on lets PostgreSQL skip the bad pages and read the rest of the table. You will lose the rows stored on the corrupt pages, but the remaining data is preserved. For a full-table corruption, a backup or replica is the only reliable path to recovery.
Does XX001 mean my entire database is corrupt?
Not necessarily. PostgreSQL raises XX001 on the specific page it cannot read. The corruption is often limited to a single table or index. The first step is to identify exactly which file and block are affected, then assess whether the problem is isolated or widespread.
How do I prevent data corruption in PostgreSQL?
Enable data_checksums at cluster creation time — this allows PostgreSQL to detect silent storage corruption early. Use reliable hardware with ECC RAM, avoid disabling fsync, ensure proper shutdown procedures, and take regular backups tested with pg_restore or pg_dump round-trips. Use pg_amcheck periodically on production clusters.
Why did this error appear after I migrated to a new server?
A common cause is copying data files while PostgreSQL was running, or failing to perform a clean shutdown before copying. Another cause is restoring a pg_basebackup snapshot without applying all required WAL. Both leave the data directory in a state that PostgreSQL flags as corrupted on startup or first access.