benbjohnson/litestream · error

close L0 file: %w

Error message

close L0 file: %w

What it means

After fsync, Litestream closes the temp file with tmpFile.Close(). This error wraps a failure of that close, indicating pending buffered data could not be flushed by the OS or the file descriptor is in a bad state. The partially staged file is discarded and the LTX file is not adopted.

Source

Thrown at db.go:1652

	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",
		"min_txid", minTXID,
		"max_txid", maxTXID)

	return nil
}

// verify ensures the current LTX state matches where it left off from
// the real WAL. Check info.ok if verification was successful.
func (db *DB) verify(ctx context.Context, state *syncState) (info syncInfo, err error) {

View on GitHub (pinned to 4ed7a308f6)

Solutions

  1. Check dmesg/journal for block-device I/O errors on the data volume.
  2. Raise the process file-descriptor limit (ulimit -n / LimitNOFILE in systemd) if fd exhaustion is reported.
  3. Fix or replace failing storage; close errors on local disks almost always indicate hardware/driver problems.
  4. Retry after remediation — the temp file is cleaned up and the next sync refetches.

Example fix

# before: systemd service hits fd limit
# after: raise LimitNOFILE for litestream
[Service]
LimitNOFILE=65536
Defensive patterns

Strategy: try-catch

Validate before calling

// Ensure adequate fd headroom
var lim syscall.Rlimit
syscall.Getrlimit(syscall.RLIMIT_NOFILE, &lim)
if lim.Cur < 4096 { log.Printf("low RLIMIT_NOFILE: %d", lim.Cur) }

Type guard

null

Try / catch

if err := tmpFile.Close(); err != nil {
    return fmt.Errorf("close L0 file: %w", err)
} // close errors are almost always EIO: check hardware/driver logs, then retry

Prevention

When it happens

Trigger: checkDatabaseBehindReplica calls tmpFile.Close() after a successful Sync and close fails — rare, usually EIO on flush of remaining buffers, or fd exhaustion earlier in the process causing odd fd states.

Common situations: Underlying storage device errors (dmesg shows I/O errors); file descriptor limit (ulimit -n) exhausted after many open LTX files; NFS server issues delaying writeback.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


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