SQLSTATE XX001 PANIC Class XX: XX — Internal Error

data_corrupted SQLSTATE XX001 — PANIC: Could Not Write to pg_wal

PostgreSQL crashes with PANIC: could not write to file “pg_wal/0000000100000023”: No space left on device. All connections are dropped. The WAL volume is full. PostgreSQL cannot write WAL, so it canno

PG 12,13,14,15,16,17 Official docs
Last reviewed May 2026 Grounded in source
Production impact High Competency WAL & Recovery Career Own backup and recovery strategy Frequency Common

Symptoms

Conditions:

  • PostgreSQL log: PANIC: could not write to file "pg_wal/..." with No space left on device
  • All connections abruptly dropped
  • df -h shows 100% on the WAL partition (or data partition if they share)
  • PostgreSQL will not restart until space is freed
  • Inactive replication slots are often the cause of pg_wal growth

Environment

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

Root Cause

  • The only safe immediate action when pg_wal is full: free space by dropping inactive replication slots — do NOT delete files from pg_wal manually.
  • max_wal_size controls checkpoint frequency, not disk usage when replication slots hold WAL.
  • max_slot_wal_keep_size (PG13+) is the prevention: limits WAL a slot can retain before it's invalidated.
  • After freeing space, PostgreSQL recovers automatically on restart — crash recovery replays the WAL.
  • If both pg_wal and pg_base are on the same volume, temp files or table growth may also fill it.
  • Never set wal_keep_size high without also setting max_slot_wal_keep_size.

Free space by dropping inactive replication slots. Set max_slot_wal_keep_size to prevent recurrence. Restart PostgreSQL — crash recovery is automatic.

Investigation

-- BEFORE restart (investigate cause):
-- df -h $PGDATA                            ← is disk actually full?
-- du -sh $PGDATA/pg_wal/                   ← WAL directory size
-- ls $PGDATA/pg_wal/*.history              ← timeline files
-- du -sh $PGDATA/pg_wal/*.* | sort -h | tail -20

-- Check replication slots (common culprit):
SELECT slot_name, active, wal_status,
    pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), confirmed_flushed_lsn))
        AS retained_wal
FROM pg_replication_slots
ORDER BY confirmed_flushed_lsn;
-- inactive slots with large retained_wal = cause of disk full

3 more diagnostic queries

Identify the exact root cause. Find the connection leak, the bloated table, the lock holder.

Fix Now

Free space by dropping inactive replication slots. Set max_slot_wal_keep_size to prevent recurrence. Restart PostgreSQL — crash recovery is automatic.

Keep going

Related & next steps

Was this helpful?