{"record":{"id":"0bfa2ff62615e803","repo":"benbjohnson/litestream","slug":"rollback-post-checkpoint-tx-w","errorCode":null,"errorMessage":"rollback post-checkpoint tx: %w","messagePattern":"rollback post-checkpoint tx: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"db.go","lineNumber":2627,"sourceCode":"\t\t})\n\tsnapshotInfo := syncInfo{\n\t\toffset:       WALHeaderSize,\n\t\tsalt1:        binary.BigEndian.Uint32(other[16:]),\n\t\tsalt2:        binary.BigEndian.Uint32(other[20:]),\n\t\tsnapshotting: true,\n\t\treason:       \"checkpoint boundary snapshot\",\n\t}\n\tresult, err = db.sync(ctx, true, exec, snapshotInfo, 0)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"cannot snapshot after checkpoint: %w\", err)\n\t}\n\texec.applySyncResult(result)\n\n\t// Release write lock before exiting.\n\t// Use rollback() helper for consistency with releaseReadLock() and the\n\t// defer above. See issue #934.\n\tif err := rollback(tx); err != nil {\n\t\treturn false, fmt.Errorf(\"rollback post-checkpoint tx: %w\", err)\n\t}\n\n\texec.state.syncedSinceCheckpoint = false\n\treturn true, nil\n}\n\n// execCheckpoint issues a wal_checkpoint PRAGMA in the given mode and returns\n// the number of frames in the WAL as reported by the checkpoint.\nfunc (db *DB) execCheckpoint(ctx context.Context, mode string) (walFrameN int, err error) {\n\t// Ignore if there is no underlying database.\n\tif db.db == nil {\n\t\treturn 0, nil\n\t}\n\n\t// Track checkpoint metrics.\n\tt := time.Now()\n\tdefer func() {\n\t\tlabels := prometheus.Labels{\"mode\": mode}","sourceCodeStart":2609,"sourceCodeEnd":2645,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L2609-L2645","documentation":"After the boundary snapshot, Litestream releases the checkpoint's write transaction with rollback(tx) (the lock-table insert is intentionally discarded). This error wraps a failure of that rollback. Because the transaction is deliberately discarded, a rollback failure here is a hygiene problem, but leaving the write transaction open would block other writers, so it is reported.","triggerScenarios":"rollback(tx) fails after a successful boundary snapshot: the transaction was already invalidated by a driver error, the connection was closed, or the context driving the transaction was canceled.","commonSituations":"Context cancellation mid-checkpoint (app shutdown, deadline) causing the driver to abort the tx; connection dropped due to database file issues; rare driver-level errors in modernc.org/sqlite.","solutions":["Check the wrapped error; if it is 'sql: transaction has already been committed or rolled back', the tx was already torn down and the write lock is free — safe to proceed.","If a context cancellation caused it, avoid canceling the ctx passed to Litestream operations during checkpoints.","Verify no concurrent db.Close() is running during replication.","Retry; subsequent checkpoints will attempt teardown again."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"rollback post-checkpoint tx\") {\n    if strings.Contains(err.Error(), \"already been committed or rolled back\") {\n        // benign: lock already released, safe to continue\n    } else {\n        // investigate driver/connection state\n    }\n}","preventionTips":["Don't cancel the replication context mid-checkpoint.","Avoid concurrent Close() during checkpoint.","Treat 'already rolled back' variants as benign.","Keep drivers updated (modernc.org/sqlite)."],"tags":["sqlite","transaction","rollback","checkpoint"],"backgroundTag":"transaction-rollback-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"}