benbjohnson/litestream · error
cannot copy wal after checkpoint: %w
Error message
cannot copy wal after checkpoint: %w
What it means
After a non-TRUNCATE checkpoint, if the WAL frame count has not advanced beyond the pre-checkpoint count, Litestream performs a final WAL copy via verifyAndSyncWithExecutor. Failure of that copy is wrapped with this error. As with the passive variant, the checkpoint is aborted and the frames written around the checkpoint may not yet be replicated.
Source
Thrown at db.go:2577
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)
exec.state.syncedSinceCheckpoint = false
return true, nil
}
// Start a transaction. This will be promoted immediately after.
db.setSyncDiagPhase(diagPhaseCheckpointSnapshotBoundaryLock,
func(s *diagState) {
s.checkpointMode = mode
s.lastSyncedWALOffset = exec.state.lastSyncedWALOffset
})
tx, err := db.db.BeginTx(ctx, nil)
if err != nil {
return false, fmt.Errorf("begin: %w", err)
}
defer func() { _ = rollback(tx) }()
View on GitHub (pinned to 4ed7a308f6)
Solutions
- Read the wrapped error to identify the failing stage (WAL read vs LTX write to storage).
- Check and restore replica backend availability, then let the next sync retry the copy.
- Run 'litestream reset' on the database if local LTX state is inconsistent with the replica.
- Check logs just before this error for a checksum-mismatch or verify failure message identifying the offset.
Defensive patterns
Strategy: retry
Try / catch
if err != nil && strings.Contains(err.Error(), "cannot copy wal after checkpoint") {
// treat as transient; retry with backoff; reset local state if checksum errors persist
scheduleRetry(err)
} Prevention
- Keep cloud storage credentials valid and pre-expiry refreshed.
- Never run third-party tools that rewrite the WAL while Litestream runs.
- Watch logs for checksum-mismatch preceding this error.
- Plan network resilience for storage backends (retries at client level).
When it happens
Trigger: mode != CheckpointModeTruncate, walFrameN <= preCheckpointFrameN, and db.verifyAndSyncWithExecutor returns an error: WAL read error, replica client write error, or checksum/position mismatch detected during verification.
Common situations: Cloud storage outage during routine RESTART checkpoints; WAL file modified by another tool; snapshot position mismatch after a crash between the checkpoint and the copy step.
Related errors
- cannot copy wal before checkpoint: %w
- cannot copy wal after passive 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/50ff507cb0f71ec4.
Report an issue: GitHub.