{"record":{"id":"d1eafa6792a465e9","repo":"benbjohnson/litestream","slug":"cannot-copy-wal-before-checkpoint-w","errorCode":null,"errorMessage":"cannot copy wal before checkpoint: %w","messagePattern":"cannot copy wal before checkpoint: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":2487,"sourceCode":"\tdb.setSyncDiagPhase(diagPhaseCheckpointReadWALHeader,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\thdr, err := readWALHeader(db.WALPath())\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\t// Copy end of WAL before checkpoint to copy as much as possible.\n\tdb.setSyncDiagPhase(diagPhaseCheckpointCopyBefore,\n\t\tfunc(s *diagState) {\n\t\t\ts.checkpointMode = mode\n\t\t\ts.lastSyncedWALOffset = exec.state.lastSyncedWALOffset\n\t\t})\n\tresult, err := db.verifyAndSyncWithExecutor(ctx, true, exec, 0)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"cannot copy wal before checkpoint: %w\", err)\n\t}\n\texec.applySyncResult(result)\n\n\tvar barrierTx *sql.Tx\n\tif mode == CheckpointModePassive {\n\t\tbarrierTx, err = db.db.BeginTx(ctx, nil)\n\t\tif err != nil {\n\t\t\treturn false, fmt.Errorf(\"begin passive checkpoint barrier: %w\", err)\n\t\t}\n\t\tdefer func() {\n\t\t\tif barrierTx != nil {\n\t\t\t\t_ = rollback(barrierTx)\n\t\t\t}\n\t\t}()\n\n\t\tif _, err := barrierTx.ExecContext(ctx, `INSERT INTO _litestream_lock (id) VALUES (1);`); err != nil {\n\t\t\treturn false, fmt.Errorf(\"_litestream_lock: %w\", err)\n\t\t}","sourceCodeStart":2469,"sourceCodeEnd":2505,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2469-L2505","documentation":"Before a checkpoint, Litestream must copy (seal) all pending WAL frames into an LTX file via verifyAndSyncWithExecutor. This error wraps any failure in that pre-checkpoint sync, meaning the WAL could not be fully replicated before checkpointing, so the checkpoint is aborted to avoid data loss.","triggerScenarios":"In checkpointWithExecutor when the initial db.verifyAndSyncWithExecutor(ctx, true, exec, 0) returns an error — replica client I/O failure, LTX write error, or WAL read error during the copy.","commonSituations":"Object storage unreachable (S3 creds/expired tokens, network outage), disk full, corrupt WAL preventing page encoding, or replica write permission problems.","solutions":["Check the wrapped inner error: if it's storage/auth related, fix credentials/network and verify with `litestream replicate` logs","Free disk space if local LTX writes failed","Run `litestream reset` only if local LTX state is corrupt (accepting re-upload)","Retry once storage is healthy; the checkpoint will be reattempted on the next sync"],"exampleFix":"// before\n# checkpoint fails: cannot copy wal before checkpoint: s3: ... AccessDenied\n// after\n# fix IAM policy so s3:PutObject is allowed, then\nlitestream ltx -db /var/lib/db/app.db","handlingStrategy":"retry","validationCode":"// probe replica storage writability before triggering a checkpoint\nif err := replicaClientProbe(ctx); err != nil { log.Printf(\"storage unavailable: %v\", err) }","typeGuard":null,"tryCatchPattern":"// go\nif err := checkpoint(ctx, mode); err != nil {\n    if strings.Contains(err.Error(), \"cannot copy wal before checkpoint\") {\n        // inspect wrapped cause (S3 AccessDenied, ENOSPC...) and retry after fix\n    }\n}","preventionTips":["Verify IAM/storage credentials regularly (s3:PutObject etc.)","Monitor disk free space on the litestream host","Alert on replication lag so syncs don't fail silently","Test storage reachability before maintenance windows"],"tags":["checkpoint","wal","replication","sync"],"backgroundTag":"database-write-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"}