Diagnostic Queries
Symptoms
VACUUM or a scan found a tuple whose xmin predates the relation’s frozen-xid horizon — an impossible state that indicates corruption. PostgreSQL raises SQLSTATE XX001 (data_corrupted).
- A tuple’s xmin is older than relfrozenxid.
- Often surfaced by VACUUM or amcheck.
- Strong signal of heap corruption.
What the server log shows
ERROR: found xmin 1234 from before relfrozenxid 5678
CONTEXT: while scanning block 42 of relation "public.orders"
Why PostgreSQL raises this — what the manual says
As Section 24.1.5 Preventing Transaction ID Wraparound Failures explains:
relfrozenxid records the oldest unfrozen XID still present in a relation; finding a live tuple whose xmin predates that horizon should never happen, so PostgreSQL treats it as corruption of transaction-ID metadata rather than a recoverable vacuum state.
Freezing guarantees that no live tuple has an xmin older than relfrozenxid. Finding one violates that invariant — the visibility bookkeeping is corrupt — so PostgreSQL reports XX001.
Common causes
- Storage/hardware faults corrupting tuple headers.
- Bugs or unsafe recovery damaging visibility data.
- A bad restore or filesystem-level tampering.
How to fix it
- Treat as corruption: investigate hardware/storage immediately.
- Restore the affected table/database from a known-good backup.
- Use
amcheck/pg_amcheck to assess the extent of damage.
Related & next steps
Reference: PostgreSQL 18 Section 25.1 “Routine Vacuuming”.
Thanks — noted. This helps keep the database accurate.