{"record":{"id":"2788553cee9b6901","repo":"benbjohnson/litestream","slug":"s3-size-ltx-file-s-w","errorCode":null,"errorMessage":"s3: size ltx file %s: %w","messagePattern":"s3: size ltx file (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"s3/replica_client.go","lineNumber":779,"sourceCode":"// PutObject and the multipart uploader based on the object size.\nfunc (c *ReplicaClient) uploadLTX(ctx context.Context, key string, r io.Reader, partSize int64) (int64, time.Time, *string, error) {\n\t// The L0 replication path passes the local LTX file, so the size is\n\t// known up front and the body stays seekable for SDK retries.\n\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)","sourceCodeStart":761,"sourceCodeEnd":797,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/s3/replica_client.go#L761-L797","documentation":"uploadSizedLTX sizes the file by seeking to the end of the reader; when that Seek(0, io.SeekEnd) call fails, this error wraps the underlying cause. It means the io.ReadSeeker passed into the upload path could not be repositioned. This is an I/O failure on the local file, not a network/S3 problem.","triggerScenarios":"uploadLTX dispatches to uploadSizedLTX when the reader is seekable; the Seek to end fails because the underlying file descriptor is closed, the file was removed, or the reader reports an I/O error (disk issue, fd exhausted).","commonSituations":"Filesystem errors on the LTX staging file (disk full, detached network volume); a bug in calling code that passes a wrapped reader whose Seek method errors; race where the temp file is deleted before upload.","solutions":["Check the wrapped error (the %w cause) for the actual syscall failure (e.g. file not found, bad file descriptor)","Ensure the file backing the reader still exists and the disk is healthy at upload time","Pass a plain *os.File or other reliably seekable reader; avoid custom Reader wrappers that break Seek","Retry the replication cycle — transient disk conditions often clear"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"f, ok := r.(*os.File)\nif !ok {\n    return fmt.Errorf(\"upload requires a seekable *os.File, got %T\", r)\n}\nif _, err := f.Seek(0, io.SeekEnd); err != nil {\n    return fmt.Errorf(\"reader not seekable/healthy: %w\", err)\n}","typeGuard":"func isSeekableFile(r io.Reader) bool {\n    _, ok := r.(*os.File)\n    return ok\n}","tryCatchPattern":"if err != nil && strings.Contains(err.Error(), \"size ltx file\") {\n    // local I/O problem on the reader; check disk and file lifetime\n}","preventionTips":["Pass real file-backed readers to upload APIs","Keep the backing file alive for the whole upload duration","Monitor disk health and free space on the LTX staging volume"],"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"}