{"record":{"id":"d362639c4db34b28","repo":"benbjohnson/litestream","slug":"decode-ltx-header-w","errorCode":null,"errorMessage":"decode ltx header: %w","messagePattern":"decode ltx header: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":2812,"sourceCode":"\t\t\treturn 0, fmt.Errorf(\"unsupported page size: %d\", pageSize)\n\t\t}\n\t\treturn pageSize, nil\n\t}\n\tif lastErr != nil {\n\t\treturn 0, fmt.Errorf(\"read ltx header: %w\", lastErr)\n\t}\n\treturn 0, fmt.Errorf(\"no ltx file available to determine page size\")\n}\n\nfunc readPageSizeFromInfo(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (uint32, error) {\n\trc, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, ltx.HeaderSize)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"open ltx file: %w\", err)\n\t}\n\tdefer rc.Close()\n\tdec := ltx.NewDecoder(rc)\n\tif err := dec.DecodeHeader(); err != nil {\n\t\treturn 0, fmt.Errorf(\"decode ltx header: %w\", err)\n\t}\n\treturn dec.Header().PageSize, nil\n}\n\nfunc isSupportedPageSize(pageSize uint32) bool {\n\tswitch pageSize {\n\tcase 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (f *VFSFile) waitForRestorePlan() ([]*ltx.FileInfo, error) {\n\t// If write mode is enabled, don't wait - return immediately so we can\n\t// create a new database if no files exist.\n\tif f.writeEnabled {\n\t\tinfos, err := CalcRestorePlan(f.ctx, f.client, 0, time.Time{}, f.logger)","sourceCodeStart":2794,"sourceCodeEnd":2830,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L2794-L2830","documentation":"Litestream opened the LTX file but the ltx.Decoder failed while decoding its header. This means the bytes at the start of the object are not a valid LTX header — corruption, truncation, or the object is not actually an LTX file.","triggerScenarios":"readPageSizeFromInfo calling dec.DecodeHeader() on a stream whose header is invalid: partial uploads, objects overwritten/corrupted by another writer, or a non-LTX object stored under the replica prefix.","commonSituations":"Interrupted uploads leaving truncated objects; manually copied/renamed files in the bucket; version-mismatch where an old LTX format is read by a newer reader (upgrade artifacts); proxy/storage layer returning an HTML error page instead of object bytes.","solutions":["Delete the corrupted LTX object and re-replicate from the source database","Run litestream reset to clear local state and force a fresh snapshot","Verify with litestream ltx that the object parses and check its header checksum","Check LTX_FORMAT.md for format-version compatibility between writer and reader versions"],"exampleFix":"// before: truncated object fails DecodeHeader\n// after: validate remotely before restore\n$ litestream ltx -level all s3://bucket/path  # list + verify each LTX file\n$ litestream restore -output db.sqlite db.yml","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := openVFS(ctx); err != nil {\n    if strings.Contains(err.Error(), \"decode ltx header\") {\n        return quarantineAndReset(ctx) // litestream reset + re-replicate\n    }\n    return err\n}","preventionTips":["Avoid interrupting replicate mid-upload (partial objects)","Verify objects with litestream ltx before restore","Never place non-LTX files under the replica prefix","Keep writer/reader Litestream versions compatible"],"tags":["vfs","ltx","corruption","header"],"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"}