benbjohnson/litestream · error

fetch updated page %d: %w

Error message

fetch updated page %d: %w

What it means

Hydrator.ApplyUpdates fetches a changed page from the replica storage via FetchPage and wraps any failure as "fetch updated page %d". During incremental hydration, the page index reports which pages changed; each must be downloaded from the replica client. A storage/network failure fetching any page aborts the whole update application.

Source

Thrown at vfs.go:904

	// Update the first page to pretend like we are in journal mode
	if off == 0 && len(p) >= 28 {
		p[18], p[19] = 0x01, 0x01
		_, _ = rand.Read(p[24:28])
	}

	return n, nil
}

// ApplyUpdates fetches updated pages and writes them to the hydration file.
func (h *Hydrator) ApplyUpdates(ctx context.Context, updates map[uint32]ltx.PageIndexElem) error {
	h.mu.Lock()
	defer h.mu.Unlock()

	for pgno, elem := range updates {
		_, data, err := FetchPage(ctx, h.client, elem.Level, elem.MinTXID, elem.MaxTXID, elem.Offset, elem.Size)
		if err != nil {
			return fmt.Errorf("fetch updated page %d: %w", pgno, err)
		}

		off := int64(pgno-1) * int64(h.pageSize)
		if _, err := h.file.WriteAt(data, off); err != nil {
			return fmt.Errorf("write updated page %d: %w", pgno, err)
		}
	}

	return nil
}

// WritePage writes a single page to the hydration file.
func (h *Hydrator) WritePage(pgno uint32, data []byte) error {
	h.mu.Lock()
	defer h.mu.Unlock()

	off := int64(pgno-1) * int64(h.pageSize)
	if _, err := h.file.WriteAt(data, off); err != nil {

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Read the wrapped cause: network/DNS errors indicate connectivity; 404/NoSuchKey indicates the LTX file was removed by retention — lengthen retention or disable it if a lifecycle policy is in place
  2. Verify replica credentials and endpoint configuration
  3. Retry the operation — ApplyUpdates can be re-run and will re-fetch the failed page
  4. Use litestream reset for the database to rebuild hydration state from scratch if the page index is stale
Defensive patterns

Strategy: retry

Validate before calling

// verify replica reachability before long operations
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if _, err := client.Head(ctx, replicaURL); err != nil {
	// storage unreachable — fix credentials/endpoint first
}

Try / catch

err := hydrator.ApplyUpdates(ctx, updates)
if err != nil {
	if errors.Is(err, context.DeadlineExceeded) || isRetryableStorageErr(err) {
		// backoff and retry; ApplyUpdates is idempotent
	}
}

Prevention

When it happens

Trigger: Calling ApplyUpdates when the replica storage is unreachable (network outage, wrong bucket/endpoint credentials); the referenced LTX file at elem.Level/elem.MinTXID/elem.MaxTXID was deleted by retention before the fetch; context canceled mid-fetch.

Common situations: S3/GCS/Azure credentials rotated or expired; retention policy removed LTX files the local page index still references; transient network partition during long-running replication; misconfigured endpoint in replica config.

Related errors


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