{"record":{"id":"70b87d61e400a382","repo":"benbjohnson/litestream","slug":"decode","errorCode":null,"errorMessage":"decode","messagePattern":"decode","errorType":"exception","errorClass":"LTXError","httpStatus":null,"severity":"critical","filePath":"db.go","lineNumber":1702,"sourceCode":"\tinfo.snapshotting = true\n\n\tif exec.pos.TXID == 0 {\n\t\tinfo.offset = WALHeaderSize\n\t\treturn info, nil // first sync\n\t}\n\n\t// Determine last WAL offset we save from.\n\tltxPath := db.LTXPath(0, exec.pos.TXID, exec.pos.TXID)\n\tltxFile, err := os.Open(ltxPath)\n\tif err != nil {\n\t\treturn info, NewLTXError(\"open\", ltxPath, 0, uint64(exec.pos.TXID), uint64(exec.pos.TXID), err)\n\t}\n\tdefer func() { _ = ltxFile.Close() }()\n\n\tdec := ltx.NewDecoder(ltxFile)\n\tif err := dec.DecodeHeader(); err != nil {\n\t\t// Decode failure indicates corruption\n\t\tltxErr := NewLTXError(\"decode\", ltxPath, 0, uint64(exec.pos.TXID), uint64(exec.pos.TXID), fmt.Errorf(\"%w: %w\", ErrLTXCorrupted, err))\n\t\treturn info, ltxErr\n\t}\n\tinfo.offset = dec.Header().WALOffset + dec.Header().WALSize\n\tinfo.salt1 = dec.Header().WALSalt1\n\tinfo.salt2 = dec.Header().WALSalt2\n\tinfo.prevCommit = dec.Header().Commit\n\n\t// If LTX WAL offset is larger than real WAL then the WAL has been truncated.\n\tif fi, err := os.Stat(db.WALPath()); err != nil {\n\t\treturn info, fmt.Errorf(\"open wal file: %w\", err)\n\t} else if info.offset > fi.Size() {\n\t\texec.state.truncatePassiveFailed = false\n\n\t\t// If we previously synced to the exact end of the WAL, this truncation\n\t\t// is expected (normal checkpoint behavior). Reset position and continue\n\t\t// incrementally rather than triggering a full snapshot. See issue #927.\n\t\tif exec.state.syncedToWALEnd {\n\t\t\t// Read new WAL header to get current salt values","sourceCodeStart":1684,"sourceCodeEnd":1720,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L1684-L1720","documentation":"An LTX file's header could not be decoded, which litestream treats as definitive corruption of that LTX file. It is wrapped in an *LTXError tagged \"decode\" and joined with ErrLTXCorrupted so callers (and auto-recover logic) can detect corruption specifically.","triggerScenarios":"dec.DecodeHeader() fails while reading the LTX file for the current position during verifyWithExecutor — truncated/partial LTX file, non-LTX bytes at the expected path, or bit rot on disk.","commonSituations":"Process killed mid-write leaving a partial LTX file; disk full during replication (truncated file); filesystem corruption; manually copying/moving LTX files incorrectly.","solutions":["Run `litestream reset <db>` to clear corrupted local LTX state and resnapshot","Restore from the replica if the remote copy is intact (auto-recover: true automates this on repeated LTX errors)","Check for ENOSPC/disk-full events around the failure time and free space","Verify storage integrity of the LTX directory (fsck, checksums)"],"exampleFix":"// config before\nreplicas:\n  - url: s3://bucket/db\n// config after (auto-reset local corruption)\nreplicas:\n  - url: s3://bucket/db\n    auto-recover: true","handlingStrategy":"fallback","validationCode":"// before relying on local state, check LTX integrity\nf, err := os.Open(ltxPath); if err == nil {\n    dec := ltx.NewDecoder(f)\n    err = dec.DecodeHeader() // corrupt if err != nil\n}","typeGuard":"var ltxErr *litestream.LTXError\nisCorrupt := errors.As(err, &ltxErr) && errors.Is(err, litestream.ErrLTXCorrupted)","tryCatchPattern":"if err := db.Sync(ctx); err != nil {\n    var ltxErr *litestream.LTXError\n    if errors.As(err, &ltxErr) && errors.Is(err, litestream.ErrLTXCorrupted) {\n        // reset local state and resnapshot from replica\n        litestream.Reset(ctx, dbPath)\n    }\n}","preventionTips":["Enable auto-recover: true on the replica for automatic reset on LTX errors","Monitor for ENOSPC; keep free disk headroom for WAL growth","Never manually truncate or edit LTX files; use `litestream reset`"],"tags":["ltx","corruption","decode","wal"],"backgroundTag":"checksum-mismatch","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}