SQLSTATE XX000 — internal_error: Database Files Moved to Different System

SQLSTATE XX000 condition internal_error class XX — XX — Internal Error severity FATAL
Reproduced & verified on PostgreSQL 14.23, 15.18, 16.14, 17.10 and 18.4 — identical message on every version.
Last reviewed 28 May 2026 · Reproduced live with the SQL on this page.

! Symptoms Free

Conditions:

  • PostgreSQL fails to start with FATAL: database files are incompatible with server
  • postmaster.pid file exists in PGDATA (copied while server was running)
  • pg_controldata shows Database cluster state: in production (not shut down)
  • pg_wal/ directory missing or incomplete WAL files
  • Version mismatch between binaries and data directory

1 Environment & reproduce Free

Difficulty: Advanced  |  PostgreSQL versions: 12, 13, 14, 15, 16, 17

? Root cause Free

  • Copying a live PGDATA with rsync is never safe — files change during copy, producing an inconsistent snapshot. Always use pg_basebackup for live copies.
  • Deleting postmaster.pid is safe on the new server only — confirms no PostgreSQL process owns the directory.
  • pg_controldata shows in production → PostgreSQL will attempt crash recovery on start (usually succeeds if WAL is intact).
  • If WAL files are missing: start fails with PANIC: could not locate a valid checkpoint record. Need a WAL archive or a proper backup.
  • Different PostgreSQL major versions = binaries incompatible with data directory. Use pg_upgrade for major version migration.
  • File ownership/permissions must match the postgres OS user on the new server.

Delete postmaster.pid on new server. Touch recovery.signal to force recovery mode. Start PostgreSQL — it will attempt crash recovery. If WAL is intact, it succeeds. If WAL missing, restore from a clean pg_basebackup.

2 Diagnose Free

RESULT
-- Check pg_control state (run as OS command):
-- pg_controldata $PGDATA | grep "Database cluster state"
-- "in production" = was not shut down cleanly before copy
-- "shut down" = safe to start

-- Check version compatibility:
-- cat $PGDATA/PG_VERSION
-- postgres --version

-- Check for postmaster.pid:
-- ls -la $PGDATA/postmaster.pid
-- If exists: delete ONLY if certain no PostgreSQL is running on this host

-- After starting (with recovery.signal):
-- psql -c "SELECT pg_is_in_recovery();"

🔒 Diagnose deeper Pro

🔒

Find every latent occurrence before it ships

The steps above clear this incident. Pro adds the executed, verified depth that stops the whole class of bug across your fleet.

  • Catalog & log queries that surface every at-risk object before a migration ships.
  • Inspect-without-running tricks (\gdesc and friends).
  • Exactly where the log line surfaces on RDS, Azure & Cloud SQL.
  • Cross-version gotchas, verified on PostgreSQL 14–18.
Unlock Pro from $24.99/mo · or $199/yr — unlocks every Pro section.

Every Pro query on this site is executed against real PostgreSQL and verified — we never publish an untested snippet.

3 Recovery & verify Free

Delete postmaster.pid on new server. Touch recovery.signal to force recovery mode. Start PostgreSQL — it will attempt crash recovery. If WAL is intact, it succeeds. If WAL missing, restore from a clean pg_basebackup.