{"record":{"id":"a4d71d99ab65cd81","repo":"benbjohnson/litestream","slug":"fetch-page-index-w","errorCode":null,"errorMessage":"fetch page index: %w","messagePattern":"fetch page index: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":1345,"sourceCode":"\tfor _, info := range infos {\n\t\tif info.Level == level && info.MaxTXID > maxTXID {\n\t\t\tmaxTXID = info.MaxTXID\n\t\t}\n\t}\n\treturn maxTXID\n}\n\n// buildIndexMap constructs a lookup of pgno to LTX file offsets.\nfunc (f *VFSFile) buildIndexMap(ctx context.Context, infos []*ltx.FileInfo) (map[uint32]ltx.PageIndexElem, error) {\n\tindex := make(map[uint32]ltx.PageIndexElem)\n\tvar commit uint32\n\tfor _, info := range infos {\n\t\tf.logger.Debug(\"opening page index\", \"level\", info.Level, \"min\", info.MinTXID, \"max\", info.MaxTXID)\n\n\t\t// Read page index.\n\t\tidx, err := FetchPageIndex(ctx, f.client, info)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"fetch page index: %w\", err)\n\t\t}\n\n\t\t// Replace pages in overall index with new pages.\n\t\tfor k, v := range idx {\n\t\t\tf.logger.Debug(\"adding page index\", \"page\", k, \"elem\", v)\n\t\t\tindex[k] = v\n\t\t}\n\t\thdr, err := FetchLTXHeader(ctx, f.client, info)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"fetch header: %w\", err)\n\t\t}\n\t\tcommit = hdr.Commit\n\t}\n\n\tf.mu.Lock()\n\tf.commit = commit\n\tf.mu.Unlock()\n","sourceCodeStart":1327,"sourceCodeEnd":1363,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L1327-L1363","documentation":"During ResetTime/rebuildIndex, the VFS iterates the restore plan and calls FetchPageIndex to download each LTX file's page index from the replica. Any failure reading or parsing a page index (network error, missing object, corrupt LTX) is wrapped as \"fetch page index: %w\". The rebuild is aborted so no partial index is swapped in.","triggerScenarios":"Calling ResetTime (or triggering a time-travel rebuild) when a listed LTX file cannot be downloaded from the replica client — network outage, deleted object, expired credentials, or corrupted LTX file whose index cannot be decoded.","commonSituations":"S3/GCS/Azure transient network failures or throttling during rebuild; an LTX file deleted by lifecycle rules between CalcRestorePlan and FetchPageIndex; IAM credentials lacking GetObject on the bucket; partially-uploaded/corrupt LTX file.","solutions":["Check the wrapped cause (%w) to distinguish network errors from decode errors.","Retry the operation — transient network/object-store errors often resolve on retry.","Verify credentials/permissions allow reading objects from the replica bucket.","Remove or re-replicate the corrupted LTX file, or run `litestream reset` to clear bad local state."],"exampleFix":null,"handlingStrategy":"retry","validationCode":"// precheck: confirm the object is reachable before rebuild\nfor _, info := range infos {\n    if _, err := client.Open(ctx, fmt.Sprintf(\"%08x.ltx\", info.MinTXID)); err != nil {\n        return fmt.Errorf(\"LTX %d unreachable: %w\", info.MinTXID, err)\n    }\n}","typeGuard":null,"tryCatchPattern":"if err := file.ResetTime(ctx); err != nil {\n    var netErr net.Error\n    if errors.As(err, &netErr) || isRetryable(err) {\n        err = retry.Do(func() error { return file.ResetTime(ctx) }, retry.Attempts(3))\n    }\n}","preventionTips":["Use retrying HTTP/storage clients with backoff for the replica client.","Grant least-privilege read access (GetObject/ListObjects) to the replica credentials.","Monitor for LTX files deleted by lifecycle rules between listing and fetch."],"tags":["network","storage","page-index","litestream"],"backgroundTag":"http-request-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"}