{"record":{"id":"4ab5a642c1a0991c","repo":"benbjohnson/litestream","slug":"extract-timestamp-from-ltx-header-w-4ab5a6","errorCode":null,"errorMessage":"extract timestamp from LTX header: %w","messagePattern":"extract timestamp from LTX header: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"s3/replica_client.go","lineNumber":790,"sourceCode":"\treturn c.uploadStreamedLTX(ctx, key, r, partSize)\n}\n\n// uploadSizedLTX uploads from a seekable reader whose size is known.\nfunc (c *ReplicaClient) uploadSizedLTX(ctx context.Context, key string, rs io.ReadSeeker, start, partSize int64) (int64, time.Time, *string, error) {\n\tend, err := rs.Seek(0, io.SeekEnd)\n\tif err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: size ltx file %s: %w\", key, err)\n\t}\n\tsize := end - start\n\tif _, err := rs.Seek(start, io.SeekStart); err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: rewind ltx file %s: %w\", key, err)\n\t}\n\n\t// Extract timestamp from LTX header, then rewind so the upload sees the\n\t// full file.\n\thdr, _, err := ltx.PeekHeader(rs)\n\tif err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\tif _, err := rs.Seek(start, io.SeekStart); err != nil {\n\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: rewind ltx file %s: %w\", key, err)\n\t}\n\n\tinput := c.putObjectInput(key, timestamp)\n\tinput.Body = rs\n\n\tif size < partSize {\n\t\tinput.ContentLength = aws.Int64(size)\n\t\tout, err := c.s3.PutObject(ctx, input)\n\t\tif err != nil {\n\t\t\treturn 0, time.Time{}, nil, fmt.Errorf(\"s3: put object %s: %w\", key, err)\n\t\t}\n\t\treturn size, timestamp, out.ETag, nil\n\t}\n","sourceCodeStart":772,"sourceCodeEnd":808,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/s3/replica_client.go#L772-L808","documentation":"uploadSizedLTX extracts the timestamp by calling ltx.PeekHeader on the reader; if the LTX header cannot be parsed, this error wraps the parse failure. It means the bytes at the start of the file are not a valid LTX header — the file is corrupt, truncated, or not an LTX file at all. The upload is aborted to avoid replicating garbage.","triggerScenarios":"PeekHeader fails on a truncated LTX file (crash mid-write), a zero-length or partially initialized temp file, bit-rot on disk, or a file that was accidentally overwritten by non-LTX data.","commonSituations":"Host crash or power loss leaving a partial LTX file; disk corruption; buggy external tooling writing into the replica directory; restoring/moving litestream data directories incorrectly.","solutions":["Inspect the wrapped error to see the specific header parse failure","Delete the corrupt LTX file and let litestream recreate it from the live database (or run 'litestream reset' for the database to clear corrupted local LTX state)","Verify disk health (fsck / SMART) if corruption is recurring","Ensure nothing else writes into the replica's LTX directory"],"exampleFix":"// recover from a corrupt local LTX file\n$ litestream reset /path/to/db\n$ systemctl restart litestream","handlingStrategy":"validation","validationCode":"if _, _, err := ltx.PeekHeader(f); err != nil {\n    return fmt.Errorf(\"LTX file %s corrupt: %w\", path, err)\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"extract timestamp from LTX header\") {\n    // corrupt/truncated LTX file: remove it or run 'litestream reset' for the DB\n}","preventionTips":["Verify disk health if header corruption recurs","Never let external tools write into the litestream replica directory","Use 'litestream reset' to clear corrupted local LTX state"],"tags":["ltx","corruption","header","upload"],"backgroundTag":"checksum-mismatch","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"}