{"record":{"id":"cb8459bc3137f6f7","repo":"benbjohnson/litestream","slug":"s3-rewind-ltx-file-s-w","errorCode":null,"errorMessage":"s3: rewind ltx file %s: %w","messagePattern":"s3: rewind ltx file (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"s3/replica_client.go","lineNumber":783,"sourceCode":"\tif rs, ok := r.(io.ReadSeeker); ok {\n\t\tif start, err := rs.Seek(0, io.SeekCurrent); err == nil {\n\t\t\treturn c.uploadSizedLTX(ctx, key, rs, start, partSize)\n\t\t}\n\t\t// Reader does not support seeking (e.g. piped file descriptor);\n\t\t// nothing has been consumed, so treat it as an unsized stream.\n\t}\n\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)","sourceCodeStart":765,"sourceCodeEnd":801,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/s3/replica_client.go#L765-L801","documentation":"After sizing the file, uploadSizedLTX seeks back to the start offset so the upload sees the whole body; failure of this Seek produces this wrapped error. It signals the seekable reader could not be repositioned to 'start'. The upload aborts before any bytes are sent.","triggerScenarios":"Same reader as the sizing step: the Seek(start, io.SeekStart) fails because the file was closed/deleted between calls, or the ReadSeeker implementation returns an error for SeekStart positioning.","commonSituations":"Temp LTX file removed by concurrent cleanup while the upload was in flight; custom reader wrappers with limited seek support; underlying storage errors mid-sequence.","solutions":["Inspect the wrapped cause for the syscall-level reason","Keep the backing file alive for the duration of the upload (no concurrent deletion/cleanup)","Use a real file-backed ReadSeeker rather than a partially-implemented wrapper","Retry the replication; a later cycle creates a fresh reader"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"if _, err := rs.Seek(0, io.SeekStart); err != nil {\n    return fmt.Errorf(\"reader cannot rewind: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"rewind ltx file\") {\n    // repositioning failed before upload; check file lifetime and storage\n}","preventionTips":["Avoid custom io.ReadSeeker wrappers with partial Seek support","Do not delete temp LTX files while an upload is in flight","Retry replication cycles on transient local I/O errors"],"tags":["s3","io","seek","file"],"backgroundTag":"file-read-failed","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"}