benbjohnson/litestream · error

copy L0 file: %w

Error message

copy L0 file: %w

What it means

After creating the temp file, Litestream streams the remote L0 LTX file into it with io.Copy(tmpFile, reader). This error wraps a failure of that copy: either reading from the remote object or writing to local disk failed mid-stream.

Source

Thrown at db.go:1643

	reader, err := db.Replica.Client.OpenLTXFile(ctx, 0, minTXID, maxTXID, 0, 0)
	if err != nil {
		return fmt.Errorf("open remote L0 file: %w", err)
	}
	defer func() { _ = reader.Close() }()

	// Write to temp file and atomically rename
	localPath := db.LTXPath(0, minTXID, maxTXID)
	tmpPath := localPath + ".tmp"

	tmpFile, err := os.Create(tmpPath)
	if err != nil {
		return fmt.Errorf("create temp L0 file: %w", err)
	}
	defer func() { _ = os.Remove(tmpPath) }() // Clean up temp file on error

	if _, err := io.Copy(tmpFile, reader); err != nil {
		_ = tmpFile.Close()
		return fmt.Errorf("copy L0 file: %w", err)
	}

	if err := tmpFile.Sync(); err != nil {
		_ = tmpFile.Close()
		return fmt.Errorf("sync L0 file: %w", err)
	}

	if err := tmpFile.Close(); err != nil {
		return fmt.Errorf("close L0 file: %w", err)
	}

	// Atomically rename temp file to final path
	if err := os.Rename(tmpPath, localPath); err != nil {
		return fmt.Errorf("rename L0 file: %w", err)
	}
	db.invalidatePosCache()

	db.Logger.Info("fetched latest L0 file from replica",

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Check network stability to the replica endpoint (curl/aws s3 cp of the same object to test).
  2. Check local disk space — io.Copy fails with ENOSPC if the disk fills mid-stream.
  3. Retry; the temp file is removed automatically on error, so the next sync can redo the fetch cleanly.
  4. If truncation recurs, verify the object's size/etag matches expectations and check storage provider status.

Example fix

// before: single-shot fetch fails on flaky link
reader, _ := client.OpenLTXFile(ctx, 0, min, max, 0, 0)
io.Copy(tmpFile, reader)
// after: rely on litestream retry rather than manual copy — ensure sync interval allows retries
# litestream.yml
check-interval: 1m  # behind-check re-runs and refetches on next pass
Defensive patterns

Strategy: retry

Validate before calling

// Pre-check network path to storage before large fetches
resp, err := http.Head(objectURL)
if err != nil || resp.StatusCode != 200 { log.Printf("storage unreachable") }

Type guard

null

Try / catch

if _, err := io.Copy(tmpFile, reader); err != nil {
    _ = tmpFile.Close()
    return fmt.Errorf("copy L0 file: %w", err)
} // temp file auto-removed; rely on next-sync retry with backoff

Prevention

When it happens

Trigger: checkDatabaseBehindReplica's io.Copy from the OpenLTXFile reader fails mid-transfer — the object-store connection drops or times out, the remote file is shorter than expected, or the local disk fills while writing.

Common situations: Large L0 files over a slow/unstable network link to S3/GCS; object-store returning truncated bodies; proxy or load balancer cutting long connections; local disk filling during the copy.

Related errors


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