benbjohnson/litestream · error

v0.3.x time bounds: %w

Error message

v0.3.x time bounds: %w

What it means

When the replica client supports the legacy v0.3.x format (ReplicaClientV3), CalcRestoreTarget also computes time bounds from old-generation backups via TimeBoundsV3. Failure of that legacy enumeration is wrapped with this message, separate from the current-format 'created at' error, so you know which backup format's metadata could not be read.

Source

Thrown at replica.go:573

			return createdAt, updatedAt, err
		}
	}
	return createdAt, updatedAt, nil
}

// CalcRestoreTarget returns a target time restore from.
func (r *Replica) CalcRestoreTarget(ctx context.Context, opt RestoreOptions) (updatedAt time.Time, err error) {
	// Determine the replicated time bounds from LTX files.
	createdAt, updatedAt, err := r.TimeBounds(ctx)
	if err != nil {
		return time.Time{}, fmt.Errorf("created at: %w", err)
	}

	// Also check v0.3.x time bounds if client supports it.
	if client, ok := r.Client.(ReplicaClientV3); ok {
		v3CreatedAt, v3UpdatedAt, err := r.TimeBoundsV3(ctx, client)
		if err != nil {
			return time.Time{}, fmt.Errorf("v0.3.x time bounds: %w", err)
		}
		// Extend time bounds to include v0.3.x backups.
		if !v3CreatedAt.IsZero() && (createdAt.IsZero() || v3CreatedAt.Before(createdAt)) {
			createdAt = v3CreatedAt
		}
		if !v3UpdatedAt.IsZero() && (updatedAt.IsZero() || v3UpdatedAt.After(updatedAt)) {
			updatedAt = v3UpdatedAt
		}
	}

	// Skip if it does not contain timestamp.
	if !opt.Timestamp.IsZero() {
		if createdAt.IsZero() && updatedAt.IsZero() {
			return time.Time{}, fmt.Errorf("no backups found")
		}
		if opt.Timestamp.Before(createdAt) || opt.Timestamp.After(updatedAt) {
			return time.Time{}, fmt.Errorf("timestamp does not exist")
		}

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Read the wrapped cause to see which legacy generation/metadata read failed.
  2. If legacy v0.3.x backups are no longer needed, remove the legacy generations or use a client without ReplicaClientV3 support so they are skipped.
  3. Restore access to / re-upload intact legacy objects, or rely on current LTX backups only.
  4. Run `litestream ltx` to verify what the replica can currently see.
Defensive patterns

Strategy: try-catch

Validate before calling

// detect legacy support before relying on v0.3.x bounds
if _, ok := replica.Client.(litestream.ReplicaClientV3); ok {
    // legacy metadata will be read; validate legacy generations exist
}

Type guard

if v3, ok := replica.Client.(litestream.ReplicaClientV3); ok {
    // client supports legacy format
}

Try / catch

target, err := replica.CalcRestoreTarget(ctx, opt)
if err != nil && strings.Contains(err.Error(), "v0.3.x time bounds") {
    // legacy metadata unreadable: decide whether legacy backups are still needed
}

Prevention

When it happens

Trigger: CalcRestoreTarget is called on a replica whose Client implements ReplicaClientV3, and TimeBoundsV3 fails reading legacy generation metadata (missing/invalid generations, listing errors, unreadable legacy super-records).

Common situations: Replicas upgraded from litestream v0.3.x with partially deleted or damaged legacy _ generations; storage listing failures; legacy timestamps records missing or corrupted after manual storage cleanup.

Understand the failure class

Background: Database query failed: Internal Server Error 500s wrapping SQL, Prisma, and connection failures — what to check first — this error's family across 16 libraries.

Related errors


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