{"record":{"id":"f16c79107f10ce92","repo":"benbjohnson/litestream","slug":"extract-timestamp-from-ltx-header-w-f16c79","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":"webdav/replica_client.go","lineNumber":249,"sourceCode":"//\n// References:\n//   - https://github.com/studio-b12/gowebdav/issues/35 (chunked encoding issues)\n//   - https://github.com/nextcloud/server/issues/7995 (0-byte file bug)\n//   - https://evertpot.com/260/ (WebDAV chunked encoding compatibility)\nfunc (c *ReplicaClient) WriteLTXFile(ctx context.Context, level int, minTXID, maxTXID ltx.TXID, rd io.Reader) (info *ltx.FileInfo, err error) {\n\tclient, err := c.init(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfilename := litestream.LTXFilePath(c.Path, level, minTXID, maxTXID)\n\n\tvar buf bytes.Buffer\n\tteeReader := io.TeeReader(rd, &buf)\n\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// Stage to temporary file to get seekable reader with known size.\n\t// This ensures compatibility with all WebDAV servers and avoids the\n\t// unreliable chunked transfer encoding that causes silent data loss\n\t// on common configurations (Nginx+FastCGI, Lighttpd, Apache+FastCGI).\n\ttmpFile, err := os.CreateTemp(\"\", \"litestream-webdav-*.ltx\")\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"webdav: cannot create temp file: %w\", err)\n\t}\n\tdefer func() {\n\t\t_ = tmpFile.Close()\n\t\t_ = os.Remove(tmpFile.Name())\n\t}()\n\n\tfullReader := io.MultiReader(&buf, rd)\n","sourceCodeStart":231,"sourceCodeEnd":267,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/webdav/replica_client.go#L231-L267","documentation":"WriteLTXFile reads the LTX header from the incoming stream via io.TeeReader to extract the transaction timestamp. If ltx.PeekHeader fails (malformed, truncated, or unreadable header), the write is aborted and the underlying ltx error is wrapped with this message. It means the data being replicated is not a valid LTX stream at the very first bytes.","triggerScenarios":"Calling WriteLTXFile with a reader whose first bytes are not a valid LTX header: empty stream, truncated WAL->LTX conversion output, or a corrupted source stream.","commonSituations":"Disk corruption producing truncated LTX data, a proxy/middleware intercepting and altering the stream, version mismatch where an older litestream produced headers the current ltx decoder rejects.","solutions":["Verify the LTX file/stream at the source with `litestream ltx -level all` to confirm it is valid","Check the writer feeding WriteLTXFile for truncation (ensure the producer finished writing before the reader is passed)","Ensure litestream and ltx library versions match between producer and consumer","Inspect any network path between producer and litestream for data-mutating proxies"],"exampleFix":"// before\nhdr, _, err := ltx.PeekHeader(teeReader)\nif err != nil { return nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err) }\n// after\nif buf.Len() < ltx.HeaderSize { return nil, fmt.Errorf(\"extract timestamp from LTX header: stream too short (%d bytes)\", buf.Len()) }\nhdr, _, err := ltx.PeekHeader(teeReader)\nif err != nil { return nil, fmt.Errorf(\"extract timestamp from LTX header: %w\", err) }","handlingStrategy":"validation","validationCode":"data, _ := io.ReadAll(io.LimitReader(rd, ltx.HeaderSize))\nif len(data) < ltx.HeaderSize { return errors.New(\"stream too short to be a valid LTX file\") }\nif _, _, err := ltx.PeekHeader(bytes.NewReader(data)); err != nil { return fmt.Errorf(\"invalid LTX header: %w\", err) }\nrd = io.MultiReader(bytes.NewReader(data), rd)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate LTX files with `litestream ltx` before replication","Ensure producers fully write streams before passing them","Keep ltx library versions consistent across the pipeline"],"tags":["ltx","stream-corruption","replication"],"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"}