{"record":{"id":"50ff507cb0f71ec4","repo":"benbjohnson/litestream","slug":"cannot-copy-wal-after-checkpoint-w","errorCode":null,"errorMessage":"cannot copy wal after checkpoint: %w","messagePattern":"cannot copy wal after checkpoint: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":2577,"sourceCode":"\t\tresult, err = db.verifyAndSyncWithExecutor(ctx, true, exec, 0)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"cannot copy wal after passive checkpoint: %w\", err)\n\t\t}\n\t\texec.applySyncResult(result)\n\t\texec.state.syncedSinceCheckpoint = false\n\t\treturn true, nil\n\t}\n\n\t// A successful TRUNCATE checkpoint always reports zero frames because\n\t// the WAL is reset before the counters are read, so the comparison\n\t// below can never prove that no commits landed between the sealed\n\t// sync and the checkpoint taking the writer lock. Those commits are\n\t// backfilled and truncated unseen, so TRUNCATE must take the boundary\n\t// snapshot unconditionally.\n\tif mode != CheckpointModeTruncate && walFrameN <= preCheckpointFrameN {\n\t\tresult, err = db.verifyAndSyncWithExecutor(ctx, true, exec, 0)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"cannot copy wal after checkpoint: %w\", err)\n\t\t}\n\t\texec.applySyncResult(result)\n\t\texec.state.syncedSinceCheckpoint = false\n\t\treturn true, nil\n\t}\n\n\t// Start a transaction. This will be promoted immediately after.\n\tdb.setSyncDiagPhase(diagPhaseCheckpointSnapshotBoundaryLock,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\ttx, err := db.db.BeginTx(ctx, nil)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"begin: %w\", err)\n\t}\n\tdefer func() { _ = rollback(tx) }()\n","sourceCodeStart":2559,"sourceCodeEnd":2595,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2559-L2595","documentation":"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.","triggerScenarios":"mode != CheckpointModeTruncate, walFrameN <= preCheckpointFrameN, and db.verifyAndSyncWithExecutor returns an error: WAL read error, replica client write error, or checksum/position mismatch detected during verification.","commonSituations":"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.","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."],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"cannot copy wal after checkpoint\") {\n    // treat as transient; retry with backoff; reset local state if checksum errors persist\n    scheduleRetry(err)\n}","preventionTips":["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)."],"tags":["wal","checkpoint","sync","replication"],"backgroundTag":"wal-sync-failed","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}