{"record":{"id":"baca5922daadfa4f","repo":"benbjohnson/litestream","slug":"new-wal-reader-w","errorCode":null,"errorMessage":"new wal reader: %w","messagePattern":"new wal reader: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"db.go","lineNumber":1879,"sourceCode":"}\n\n// detectFullCheckpoint attempts to detect checks if a FULL or RESTART checkpoint\n// has occurred and we may have missed some frames.\nfunc (db *DB) detectFullCheckpoint(ctx context.Context, knownSalts [][2]uint32) (bool, error) {\n\twalFile, err := os.Open(db.WALPath())\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"open wal file: %w\", err)\n\t}\n\tdefer walFile.Close()\n\n\tvar lastKnownSalt [2]uint32\n\tif len(knownSalts) > 0 {\n\t\tlastKnownSalt = knownSalts[len(knownSalts)-1]\n\t}\n\n\trd, err := NewWALReader(walFile, db.Logger.With(LogKeySubsystem, LogSubsystemWALReader))\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"new wal reader: %w\", err)\n\t}\n\tm, err := rd.FrameSaltsUntil(ctx, lastKnownSalt)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"frame salts until: %w\", err)\n\t}\n\n\t// Remove known salts from the map.\n\tfor _, salt := range knownSalts {\n\t\tdelete(m, salt)\n\t}\n\n\t// If we have more than one unknown salt, then we have a FULL or RESTART checkpoint.\n\treturn len(m) >= 1, nil\n}\n\ntype syncInfo struct {\n\toffset              int64 // end of the previous LTX read\n\tsalt1               uint32","sourceCodeStart":1861,"sourceCodeEnd":1897,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/db.go#L1861-L1897","documentation":"NewWALReader failed to parse the WAL header when detectFullCheckpoint validates the WAL. Litestream requires a valid 32-byte WAL header (magic, format version, page size, checkpoint sequence); a malformed or empty header makes the reader unusable.","triggerScenarios":"NewWALReader(walFile, logger) returns an error: WAL file smaller than WALHeaderSize, invalid WAL magic bytes (not 0x377f0682/0x377f0683), or unsupported page size in the header.","commonSituations":"A non-SQLite or corrupted file at the WAL path; WAL truncated mid-header by a crash or disk-full condition; an SQLite version/pragma producing an unsupported WAL header; file zeroed by storage failure.","solutions":["Inspect the first 32 bytes of the -wal file to confirm valid SQLite WAL magic","Trigger a checkpoint (PRAGMA wal_checkpoint(TRUNCATE)) or restart the app so SQLite rewrites a fresh WAL","Restore the WAL file from backup if it is corrupt","Verify the disk is not full / filesystem not corrupt"],"exampleFix":"// before\n$ xxd /data/app.db-wal | head -1\n00000000: 0000 0000 0000 0000 ...   (zeroed header)\n// after\n$ sqlite3 /data/app.db 'PRAGMA wal_checkpoint(TRUNCATE);'  # regenerates a valid WAL","handlingStrategy":"validation","validationCode":"hdr, err := os.ReadFile(walPath)\nvalid := err == nil && len(hdr) >= 32 && (binary.BigEndian.Uint32(hdr[0:4]) == 0x377f0682 || binary.BigEndian.Uint32(hdr[0:4]) == 0x377f0683)","typeGuard":null,"tryCatchPattern":"if err != nil {\n\tif strings.Contains(err.Error(), \"new wal reader\") {\n\t\t// WAL header corrupt: checkpoint via the app, then restart litestream\n\t\trun(\"sqlite3 \"+dbPath+\" 'PRAGMA wal_checkpoint(TRUNCATE);'\")\n\t}\n}","preventionTips":["Protect the DB volume from power loss (fsync-capable storage) to avoid zeroed WAL headers","Never edit or truncate -wal files manually","Use `litestream reset` after restoring from inconsistent snapshots","Keep SQLite and Litestream versions current"],"tags":["sqlite","wal","corruption"],"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"}