{"record":{"id":"f060dfdf852411a2","repo":"benbjohnson/litestream","slug":"extract-timestamp-from-ltx-header-w-f060df","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":"oss/replica_client.go","lineNumber":245,"sourceCode":"\treturn result.Body, nil\n}\n\n// WriteLTXFile writes an LTX file to the replica.\n// Extracts timestamp from LTX header and stores it in OSS metadata to preserve original creation time.\n// Uses multipart upload for large files via the uploader.\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, r io.Reader) (*ltx.FileInfo, error) {\n\tif err := c.Init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Use TeeReader to peek at LTX header while preserving data for upload\n\tvar buf bytes.Buffer\n\tteeReader := io.TeeReader(r, &buf)\n\n\t// Extract timestamp from LTX header\n\thdr, _, err := ltx.PeekHeader(teeReader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err)\n\t}\n\ttimestamp := time.UnixMilli(hdr.Timestamp).UTC()\n\n\t// Combine buffered data with rest of reader\n\trc := internal.NewReadCounter(io.MultiReader(&buf, r))\n\n\tfilename := ltx.FormatFilename(minTXID, maxTXID)\n\tkey := c.ltxPath(level, filename)\n\n\t// Store timestamp in OSS metadata for accurate timestamp retrieval\n\tmetadata := map[string]string{\n\t\tMetadataKeyTimestamp: timestamp.Format(time.RFC3339Nano),\n\t}\n\n\t// Use uploader for automatic multipart handling (files >5GB)\n\tresult, err := c.uploader.UploadFrom(ctx, &oss.PutObjectRequest{\n\t\tBucket:   oss.Ptr(c.Bucket),\n\t\tKey:      oss.Ptr(key),","sourceCodeStart":227,"sourceCodeEnd":263,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/oss/replica_client.go#L227-L263","documentation":"WriteLTXFile peeks at the LTX header of the incoming stream to extract the transaction timestamp. If the reader does not begin with a valid LTX header, PeekHeader fails and this error wraps the cause. Because the reader is TeeReader'd, this happens before any bytes are uploaded.","triggerScenarios":"Passing a reader to WriteLTXFile whose first bytes are not a valid ltx.Header: truncated WAL-to-LTX conversion output, corrupted file on disk, or a caller (custom code/tests) feeding a non-LTX stream.","commonSituations":"Disk corruption or partial write of the local LTX file before upload; running a mismatched/older litestream version that produced an incompatible header; third-party tooling writing into the replica path.","solutions":["Validate the local LTX file with 'litestream ltx <file>' to confirm header integrity.","If the local LTX state is corrupted, run 'litestream reset <db>' to clear it and let replication restart.","Upgrade litestream so the writer and replica client use the same LTX format version.","If this comes from custom code, ensure you pass the raw LTX stream (never a pre-opened/partially consumed reader)."],"exampleFix":"// before\nf, _ := os.Open(corruptPath)\ninfo, err := client.WriteLTXFile(ctx, key, metadata, f, size)\n// after\nif _, _, err := ltx.PeekHeader(f); err != nil {\n    return fmt.Errorf(\"local ltx file corrupt, run litestream reset: %w\", err)\n}\nf.Seek(0, io.SeekStart)\ninfo, err := client.WriteLTXFile(ctx, key, metadata, f, size)","handlingStrategy":"validation","validationCode":"// Verify the stream starts with a valid LTX header before uploading.\nif _, _, err := ltx.PeekHeader(reader); err != nil {\n    return fmt.Errorf(\"invalid ltx stream: %w\", err)\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Never write non-Litestream data into the LTX replica path.","Keep writer and replica client on the same litestream version to avoid format drift.","Monitor disk health; corruption of local LTX files surfaces here first."],"tags":["ltx","corruption","oss","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"}