benbjohnson/litestream · error

page not found: %d

Error message

page not found: %d

What it means

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.

Source

Thrown at vfs.go:1586

	// Get page index element
	f.mu.Lock()
	elem, ok := f.index[pgno]
	writeEnabled := f.writeEnabled // capture while holding lock to avoid data race
	f.mu.Unlock()

	if !ok {
		// For write-enabled VFS with a new database (no existing pages),
		// return zeros to indicate empty page. SQLite will initialize the database.
		if writeEnabled {
			f.logger.Debug("page not found, returning zeros for new database", "page", pgno)
			for i := range p {
				p[i] = 0
			}
			return len(p), nil
		}
		f.logger.Error("page not found", "page", pgno)
		return 0, fmt.Errorf("page not found: %d", pgno)
	}

	var data []byte
	var lastErr error
	ctx := f.ctx
	for attempt := 0; attempt < pageFetchRetryAttempts; attempt++ {
		_, data, lastErr = FetchPage(ctx, f.client, elem.Level, elem.MinTXID, elem.MaxTXID, elem.Offset, elem.Size)
		if lastErr == nil {
			break
		}
		if !isRetryablePageError(lastErr) {
			f.logger.Error("cannot fetch page", "page", pgno, "attempt", attempt+1, "error", lastErr)
			return 0, fmt.Errorf("fetch page: %w", lastErr)
		}

		if attempt == pageFetchRetryAttempts-1 {
			f.logger.Error("cannot fetch page after retries", "page", pgno, "attempts", pageFetchRetryAttempts, "error", lastErr)
			return 0, sqlite3vfs.BusyError

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Confirm the read offset/pgno is within the file size implied by the commit header.
  2. Call ResetTime / re-hydrate to rebuild the page index from the replica.
  3. Check that the database was fully replicated (all LTX levels present) via `litestream ltx -level all`.
  4. If it recurs at the same pgno, validate LTX integrity — the index may be corrupt.
Defensive patterns

Strategy: validation

Validate before calling

// guard reads against the commit boundary before issuing them
if int64(off)+int64(len(p)) > int64(f.commit)*int64(f.pageSize) {
    return 0, io.EOF // do not ask the VFS for pages beyond commit
}

Try / catch

n, err := file.ReadAt(p, off)
if err != nil && strings.Contains(err.Error(), "page not found") {
    if rerr := file.ResetTime(ctx); rerr != nil { return rerr }
    n, err = file.ReadAt(p, off) // retry after index rebuild
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06). Data as JSON: /api/errors/1e9171efab2ce101. Report an issue: GitHub.