{"record":{"id":"1e9171efab2ce101","repo":"benbjohnson/litestream","slug":"page-not-found-d","errorCode":null,"errorMessage":"page not found: %d","messagePattern":"page not found: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"vfs.go","lineNumber":1586,"sourceCode":"\n\t// Get page index element\n\tf.mu.Lock()\n\telem, ok := f.index[pgno]\n\twriteEnabled := f.writeEnabled // capture while holding lock to avoid data race\n\tf.mu.Unlock()\n\n\tif !ok {\n\t\t// For write-enabled VFS with a new database (no existing pages),\n\t\t// return zeros to indicate empty page. SQLite will initialize the database.\n\t\tif writeEnabled {\n\t\t\tf.logger.Debug(\"page not found, returning zeros for new database\", \"page\", pgno)\n\t\t\tfor i := range p {\n\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","sourceCodeStart":1568,"sourceCodeEnd":1604,"githubUrl":"https://github.com/benbjohnson/litestream/blob/4ed7a308f6271ebfd2b0a6e4b70b03011a37e4a3/vfs.go#L1568-L1604","documentation":"When reading a page, the VFS looks up pgno in its page index; if the page is absent and cannot be satisfied (beyond commit, or not in any LTX index), it logs \"page not found\" and returns an error. This means the page index has no record of the requested page, so SQLite asked for a page the replica chain never contained.","triggerScenarios":"A Read for a pgno whose entry is missing from the built index — reads past the commit boundary, a corrupted/incomplete index after ResetTime, or SQLite reading a page from a WAL/journal offset the VFS index never captured.","commonSituations":"Time-travel reads with a stale or partially built index; index rebuild raced with new writes; SQLite asserting a larger database size than the replica's commit header; bugs after a failed rebuild left a truncated index.","solutions":["Confirm the read offset/pgno is within the file size implied by the commit header.","Call ResetTime / re-hydrate to rebuild the page index from the replica.","Check that the database was fully replicated (all LTX levels present) via `litestream ltx -level all`.","If it recurs at the same pgno, validate LTX integrity — the index may be corrupt."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// guard reads against the commit boundary before issuing them\nif int64(off)+int64(len(p)) > int64(f.commit)*int64(f.pageSize) {\n    return 0, io.EOF // do not ask the VFS for pages beyond commit\n}","typeGuard":null,"tryCatchPattern":"n, err := file.ReadAt(p, off)\nif err != nil && strings.Contains(err.Error(), \"page not found\") {\n    if rerr := file.ResetTime(ctx); rerr != nil { return rerr }\n    n, err = file.ReadAt(p, off) // retry after index rebuild\n}","preventionTips":["Call ResetTime after replication catch-up so the index matches the replica.","Verify full LTX chain integrity with `litestream ltx -level all`.","Never serve reads from a partially built or interrupted index rebuild."],"tags":["page-index","read-path","litestream"],"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-14T05:17:10.506Z"}