{"record":{"id":"f38cdd8d1d30936b","repo":"benbjohnson/litestream","slug":"fetch-page-w","errorCode":null,"errorMessage":"fetch page: %w","messagePattern":"fetch page: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":1599,"sourceCode":"\t\t\t\tp[i] = 0\n\t\t\t}\n\t\t\treturn len(p), nil\n\t\t}\n\t\tf.logger.Error(\"page not found\", \"page\", pgno)\n\t\treturn 0, fmt.Errorf(\"page not found: %d\", pgno)\n\t}\n\n\tvar data []byte\n\tvar lastErr error\n\tctx := f.ctx\n\tfor attempt := 0; attempt < pageFetchRetryAttempts; attempt++ {\n\t\t_, data, lastErr = FetchPage(ctx, f.client, elem.Level, elem.MinTXID, elem.MaxTXID, elem.Offset, elem.Size)\n\t\tif lastErr == nil {\n\t\t\tbreak\n\t\t}\n\t\tif !isRetryablePageError(lastErr) {\n\t\t\tf.logger.Error(\"cannot fetch page\", \"page\", pgno, \"attempt\", attempt+1, \"error\", lastErr)\n\t\t\treturn 0, fmt.Errorf(\"fetch page: %w\", lastErr)\n\t\t}\n\n\t\tif attempt == pageFetchRetryAttempts-1 {\n\t\t\tf.logger.Error(\"cannot fetch page after retries\", \"page\", pgno, \"attempts\", pageFetchRetryAttempts, \"error\", lastErr)\n\t\t\treturn 0, sqlite3vfs.BusyError\n\t\t}\n\n\t\tdelay := pageFetchRetryDelay * time.Duration(attempt+1)\n\t\tf.logger.Warn(\"transient page fetch error, retrying\", \"page\", pgno, \"attempt\", attempt+1, \"delay\", delay, \"error\", lastErr)\n\n\t\ttimer := time.NewTimer(delay)\n\t\tselect {\n\t\tcase <-timer.C:\n\t\tcase <-f.ctx.Done():\n\t\t\ttimer.Stop()\n\t\t\treturn 0, fmt.Errorf(\"fetch page: %w\", lastErr)\n\t\t}\n\t\ttimer.Stop()","sourceCodeStart":1581,"sourceCodeEnd":1617,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L1581-L1617","documentation":"FetchPage retrieves a page's bytes from the replica at a recorded level/txid/offset. On a non-retryable error the read fails immediately with \"fetch page: %w\"; only transient errors are retried (and eventually surface as BusyError). This error wraps the underlying storage/transport failure verbatim.","triggerScenarios":"A page read requiring a remote fetch where the replica client returns a permanent error: 404 (object deleted), 403 (bad credentials), range errors from a corrupt offset/size in the index, or malformed responses.","commonSituations":"Lifecycle policies deleting LTX objects while the index still references them; expired/rotated cloud credentials; provider incompatibility (non-S3 endpoints rejecting Range requests); corrupted index offsets after a bad rebuild.","solutions":["Inspect the wrapped cause: 404 → object gone, 403 → credentials, range error → corrupt index.","Fix credentials/bucket permissions so GetObject (with Range) succeeds.","Rebuild the page index (ResetTime) if index offsets no longer match stored objects.","Prevent lifecycle deletion of in-use LTX files or disable client-side retention if a cloud lifecycle policy manages cleanup."],"exampleFix":"// before: lifecycle deletes objects the index still references\nLifecycle: { Expire: \"1d\" } // index entries older than 1d 404\n\n// after: manage cleanup with litestream retention, not storage expiry\n// (remove bucket lifecycle rule; keep Store.RetentionEnabled default)","handlingStrategy":"retry","validationCode":"// preflight: credentials and object reachability\nif _, err := client.Head(ctx, objectPath); err != nil {\n    return fmt.Errorf(\"replica object unavailable before read: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"if _, err := file.ReadAt(p, off); err != nil {\n    var s3err awserr.RequestFailure\n    if errors.As(err, &s3err) && s3err.StatusCode() == 404 {\n        // LTX object gone: rebuild index or re-replicate\n    }\n}","preventionTips":["Rotate credentials before expiry; test GetObject (with Range) access.","Disable storage lifecycle rules that delete LTX objects referenced by the index.","Review docs/PROVIDER_COMPATIBILITY.md for S3-compatible endpoints with Range quirks."],"tags":["network","storage","page-fetch","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-14T00:17:10.932Z"}