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

  1. Read the wrapped error to identify the failing stage (WAL read vs LTX write to storage).
  2. Check and restore replica backend availability, then let the next sync retry the copy.
  3. Run 'litestream reset' on the database if local LTX state is inconsistent with the replica.
  4. 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

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


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/50ff507cb0f71ec4. Report an issue: GitHub.