benbjohnson/litestream · error

list ltx files for catch-up: %w

Error message

list ltx files for catch-up: %w

What it means

Hydrator.CatchUp lists LTX files from the replica starting at fromTXID+1 (level 0) to apply everything up to toTXID. This error wraps a failure of client.LTXFiles — the listing request itself failed before any files could be applied.

Source

Thrown at vfs.go:817

	defer h.mu.Unlock()

	dec := ltx.NewDecoder(pr)
	if err := dec.DecodeDatabaseTo(h.file); err != nil {
		return fmt.Errorf("decode database: %w", err)
	}

	h.txid = infos[len(infos)-1].MaxTXID
	return nil
}

// CatchUp applies updates from LTX files between fromTXID and toTXID.
func (h *Hydrator) CatchUp(ctx context.Context, fromTXID, toTXID ltx.TXID) error {
	h.logger.Debug("catching up hydration", "from", fromTXID, "to", toTXID)

	// Fetch LTX files from fromTXID+1 to toTXID
	itr, err := h.client.LTXFiles(ctx, 0, fromTXID+1, false)
	if err != nil {
		return fmt.Errorf("list ltx files for catch-up: %w", err)
	}
	defer itr.Close()

	for itr.Next() {
		info := itr.Item()
		if info.MaxTXID > toTXID {
			break
		}

		if err := h.ApplyLTX(ctx, info); err != nil {
			return fmt.Errorf("apply ltx to hydrated file: %w", err)
		}

		h.mu.Lock()
		h.txid = info.MaxTXID
		h.mu.Unlock()
	}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Unwrap the error for the backend cause; fix credentials, endpoint, or network and retry.
  2. Implement retry with backoff for transient listing failures (throttling/timeout).
  3. Verify the replica client config (bucket, prefix, region) matches where litestream is actually writing.
  4. If listing consistently fails, fall back to a full Restore once connectivity is restored, since CatchUp cannot proceed without the listing.

Example fix

// before
itr, err := h.client.LTXFiles(ctx, 0, fromTXID+1, false)
if err != nil { return err }
// after
itr, err := h.client.LTXFiles(ctx, 0, fromTXID+1, false)
if err != nil {
    if isTransient(err) { return retryWithBackoff(...) }
    return fmt.Errorf("list ltx files for catch-up: %w", err)
}
Defensive patterns

Strategy: retry

Validate before calling

// probe the replica before catch-up
if _, err := client.LTXFiles(ctx, 0, 1, false); err != nil {
    return fmt.Errorf("replica unreachable: %w", err)
}

Try / catch

itr, err := h.client.LTXFiles(ctx, 0, fromTXID+1, false)
if err != nil {
    if isRetryable(err) { return backoffRetry(ctx) }
    return fmt.Errorf("list ltx files for catch-up: %w", err)
}

Prevention

When it happens

Trigger: Calling CatchUp when the replica backend is unreachable, credentials are invalid, the bucket/container doesn't exist, or a transient network/API error (throttling, 5xx) occurs during the listing call.

Common situations: S3/GCS/Azure rate limiting during heavy sync; expired cloud credentials; DNS or VPN outage on the host; misconfigured replica path after moving the database.

Understand the failure class

Background: "API request failed": what wrapped HTTP errors from external APIs mean and how to find the real cause — this error's family across 29 libraries.

Related errors


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