benbjohnson/litestream · error
cannot copy wal after passive checkpoint: %w
Error message
cannot copy wal after passive checkpoint: %w
What it means
After a PASSIVE checkpoint, Litestream must copy any WAL frames that accumulated since the checkpoint back into its replication stream via verifyAndSyncWithExecutor. If that sync fails, the checkpoint is reported as failed with this error. It means WAL frames written after the checkpoint were not captured and replication state is now inconsistent until a successful sync.
Source
Thrown at db.go:2561
// If WAL hasn't been restarted, exit.
db.setSyncDiagPhase(diagPhaseCheckpointVerifyRestart,
func(s *diagState) {
s.checkpointMode = mode
s.lastSyncedWALOffset = exec.state.lastSyncedWALOffset
})
other, err := readWALHeader(db.WALPath())
if err != nil {
return false, err
} else if bytes.Equal(hdr, other) {
exec.state.syncedSinceCheckpoint = false
return false, nil
}
exec.state.truncatePassiveFailed = false
if mode == CheckpointModePassive {
result, err = db.verifyAndSyncWithExecutor(ctx, true, exec, 0)
if err != nil {
return false, fmt.Errorf("cannot copy wal after passive checkpoint: %w", err)
}
exec.applySyncResult(result)
exec.state.syncedSinceCheckpoint = false
return true, nil
}
// A successful TRUNCATE checkpoint always reports zero frames because
// the WAL is reset before the counters are read, so the comparison
// below can never prove that no commits landed between the sealed
// sync and the checkpoint taking the writer lock. Those commits are
// backfilled and truncated unseen, so TRUNCATE must take the boundary
// snapshot unconditionally.
if mode != CheckpointModeTruncate && walFrameN <= preCheckpointFrameN {
result, err = db.verifyAndSyncWithExecutor(ctx, true, exec, 0)
if err != nil {
return false, fmt.Errorf("cannot copy wal after checkpoint: %w", err)
}
exec.applySyncResult(result)View on GitHub (pinned to 4ed7a308f6)
Solutions
- Look at the wrapped error to determine whether the failure is on the WAL read side or the replica storage write side.
- Verify replica storage connectivity/credentials (e.g. S3 bucket reachable) and retry the sync.
- If the WAL appears corrupt or state is inconsistent, use 'litestream reset' for the database to rebuild local LTX state.
- Enable 'auto-recover' on the replica if you want Litestream to reset local state automatically on LTX errors.
Defensive patterns
Strategy: retry
Validate before calling
if err := replicaClientHealthCheck(ctx); err != nil { /* verify storage backend reachable before syncing */ } Try / catch
if err != nil && strings.Contains(err.Error(), "cannot copy wal after passive checkpoint") {
log.Printf("post-checkpoint WAL copy failed: %v", err)
// backoff and retry; consider litestream reset if persistent
} Prevention
- Monitor replica storage reachability and credentials expiry.
- Do not truncate or modify the WAL file externally.
- Enable 'auto-recover' on the replica for automatic state reset on LTX errors.
- Alert on consecutive sync failures.
When it happens
Trigger: mode == CheckpointModePassive and db.verifyAndSyncWithExecutor(ctx, true, exec, 0) returns an error immediately after the passive checkpoint: WAL read failure, checksum mismatch in the WAL, LTX write failure to the replica storage, or SQLITE_BUSY reading the db.
Common situations: Replica storage backend (S3, file, etc.) temporarily unavailable or returning errors during the post-checkpoint copy; corrupt or externally truncated WAL file; network interruption to cloud storage mid-sync.
Related errors
- cannot copy wal before checkpoint: %w
- cannot copy wal after checkpoint: %w
- cannot snapshot after checkpoint: %w
- checkpoint: %w
- checkpoint failed: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/ea44652578c9d297.
Report an issue: GitHub.