benbjohnson/litestream · error
fetch page index: %w
Error message
fetch page index: %w
What it means
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.
Source
Thrown at vfs.go:1345
for _, info := range infos {
if info.Level == level && info.MaxTXID > maxTXID {
maxTXID = info.MaxTXID
}
}
return maxTXID
}
// buildIndexMap constructs a lookup of pgno to LTX file offsets.
func (f *VFSFile) buildIndexMap(ctx context.Context, infos []*ltx.FileInfo) (map[uint32]ltx.PageIndexElem, error) {
index := make(map[uint32]ltx.PageIndexElem)
var commit uint32
for _, info := range infos {
f.logger.Debug("opening page index", "level", info.Level, "min", info.MinTXID, "max", info.MaxTXID)
// Read page index.
idx, err := FetchPageIndex(ctx, f.client, info)
if err != nil {
return nil, fmt.Errorf("fetch page index: %w", err)
}
// Replace pages in overall index with new pages.
for k, v := range idx {
f.logger.Debug("adding page index", "page", k, "elem", v)
index[k] = v
}
hdr, err := FetchLTXHeader(ctx, f.client, info)
if err != nil {
return nil, fmt.Errorf("fetch header: %w", err)
}
commit = hdr.Commit
}
f.mu.Lock()
f.commit = commit
f.mu.Unlock()
View on GitHub (pinned to 4ed7a308f6)
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.
Defensive patterns
Strategy: retry
Validate before calling
// precheck: confirm the object is reachable before rebuild
for _, info := range infos {
if _, err := client.Open(ctx, fmt.Sprintf("%08x.ltx", info.MinTXID)); err != nil {
return fmt.Errorf("LTX %d unreachable: %w", info.MinTXID, err)
}
} Try / catch
if err := file.ResetTime(ctx); err != nil {
var netErr net.Error
if errors.As(err, &netErr) || isRetryable(err) {
err = retry.Do(func() error { return file.ResetTime(ctx) }, retry.Attempts(3))
}
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.
Related errors
- fetch header: %w
- fetch page: %w
- abs: cannot delete ltx file %q: %w
- abs: cannot list blobs: %w
- abs: cannot delete blob %q: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/a4d71d99ab65cd81.
Report an issue: GitHub.