{"record":{"id":"757b9d74997f7b22","repo":"benbjohnson/litestream","slug":"open-ltx-file-w-757b9d","errorCode":null,"errorMessage":"open ltx file: %w","messagePattern":"open ltx file: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"replica_client.go","lineNumber":104,"sourceCode":"// DefaultEstimatedPageIndexSize is size that is first fetched when fetching the page index.\n// If the fetch was smaller than the actual page index, another call is made to fetch the rest.\nconst DefaultEstimatedPageIndexSize = 32 * 1024 // 32KB\n\nfunc FetchPageIndex(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (map[uint32]ltx.PageIndexElem, error) {\n\trc, err := fetchPageIndexData(ctx, client, info)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer rc.Close()\n\n\treturn ltx.DecodePageIndex(bufio.NewReader(rc), info.Level, info.MinTXID, info.MaxTXID)\n}\n\n// FetchLTXHeader reads & returns the LTX header for the given file info.\nfunc FetchLTXHeader(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (ltx.Header, error) {\n\trc, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, ltx.HeaderSize)\n\tif err != nil {\n\t\treturn ltx.Header{}, fmt.Errorf(\"open ltx file: %w\", err)\n\t}\n\tdefer rc.Close()\n\thdr, _, err := ltx.PeekHeader(rc)\n\tif err != nil {\n\t\treturn ltx.Header{}, fmt.Errorf(\"peek header: %w\", err)\n\t}\n\treturn hdr, nil\n}\n\n// fetchPageIndexData fetches a chunk of the end of the file to get the page index.\n// If the fetch was smaller than the actual page index, another call is made to fetch the rest.\nfunc fetchPageIndexData(ctx context.Context, client ReplicaClient, info *ltx.FileInfo) (io.ReadCloser, error) {\n\t// Fetch the end of the file to get the page index.\n\toffset := info.Size - DefaultEstimatedPageIndexSize\n\tif offset < 0 {\n\t\toffset = 0\n\t}\n","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/replica_client.go#L86-L122","documentation":"FetchLTXHeader opens a specific LTX file on the replica (client.OpenLTXFile, restricted to the header bytes) and this error wraps the open failure. Per the RAISED-IN note, it surfaces through Init and related flows (restoreIfNotExists, LTXFiles, WriteLTXFile, OpenLTXFile, DeleteLTXFiles, DeleteAll), meaning an LTX file recorded in the listing could not actually be opened for reading.","triggerScenarios":"OpenLTXFile called with (level, MinTXID, MaxTXID) that does not resolve to a stored object: the file was deleted by retention/compaction between listing and open, the object name is wrong, storage credentials lack read access, or the object is missing in the bucket/container.","commonSituations":"A retention job or manual cleanup deleted LTX files while a restore/validation was in progress; replica URL points to a bucket where compaction removed the referenced generation; misconfigured endpoint causing 404-style open failures; IAM policy missing s3:GetObject.","solutions":["Re-list the replica (litestream ltx / ValidateLevel) to get current file info — the referenced file may have been removed by retention or compaction","Verify storage credentials allow object reads (e.g. s3:GetObject) and the bucket/endpoint config is correct","If local state is inconsistent, run 'litestream reset' for the database or perform a fresh 'litestream restore'","Check for concurrent processes deleting from the same replica (single replica per database rule)"],"exampleFix":"// before: opening a FileInfo captured long ago\nrc, err := client.OpenLTXFile(ctx, staleInfo.Level, staleInfo.MinTXID, staleInfo.MaxTXID, 0, ltx.HeaderSize)\n// after: re-fetch fresh file info right before opening\ninfo, err := findFile(ctx, client, level, txid) // fresh listing\nrc, err := client.OpenLTXFile(ctx, info.Level, info.MinTXID, info.MaxTXID, 0, ltx.HeaderSize)","handlingStrategy":"retry","validationCode":"// Re-list to confirm the file still exists before opening\nitr, err := client.LTXFiles(ctx, info.Level, info.MinTXID, false)\nif err == nil {\n    var found bool\n    for itr.Next() { if itr.Item().MaxTXID == info.MaxTXID { found = true; break } }\n    if !found { return fmt.Errorf(\"LTX file %s no longer present\", info.MaxTXID) }\n}","typeGuard":"func fileStillListed(ctx context.Context, c ReplicaClient, info *ltx.FileInfo) bool {\n    itr, err := c.LTXFiles(ctx, info.Level, info.MinTXID, false)\n    if err != nil { return false }\n    defer itr.Close()\n    for itr.Next() {\n        it := itr.Item()\n        if it.MinTXID == info.MinTXID && it.MaxTXID == info.MaxTXID { return true }\n    }\n    return false\n}","tryCatchPattern":"hdr, err := FetchLTXHeader(ctx, client, info)\nif err != nil {\n    if errors.Is(err, os.ErrNotExist) || isNotFoundErr(err) {\n        // re-list and pick a fresher restore candidate\n    }\n    return err\n}","preventionTips":["Do not run retention cleanup concurrently with restore/validation","Always work from a freshly fetched file listing, not cached FileInfo","Verify storage read permissions (e.g. s3:GetObject) in IAM policy","Use 'litestream reset' when local state references missing objects"],"tags":["storage","replica-client","restore","file-not-found"],"backgroundTag":"resource-not-found","analyzedSha":"4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3","analyzedAt":"2026-09-06T18:29:25.564Z","contentChangedAt":"2026-09-06T18:29:25.564Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}