benbjohnson/litestream · error
close txid file: %w
Error message
close txid file: %w
What it means
WriteTXIDFile wraps f.Close() as 'close txid file: %w'. Close is called after fsync to release the file descriptor for the temp file before the atomic rename. A close error means buffered metadata could not be committed or the descriptor could not be released, so the temp file may be incomplete and the TXID is NOT promoted to the final '<outputPath>-txid' path.
Source
Thrown at replica.go:1740
tmpPath := txidPath + ".tmp"
f, err := os.Create(tmpPath)
if err != nil {
return fmt.Errorf("create txid temp file: %w", err)
}
defer f.Close()
defer os.Remove(tmpPath)
if _, err := fmt.Fprintln(f, txid); err != nil {
return fmt.Errorf("write txid: %w", err)
}
if err := f.Sync(); err != nil {
return fmt.Errorf("sync txid file: %w", err)
}
if err := f.Close(); err != nil {
return fmt.Errorf("close txid file: %w", err)
}
if err := os.Rename(tmpPath, txidPath); err != nil {
return fmt.Errorf("rename txid file: %w", err)
}
if err := internal.FsyncDir(filepath.Dir(txidPath)); err != nil {
return fmt.Errorf("sync txid dir: %w", err)
}
return nil
}
// ReadTXIDFile reads the TXID from a sidecar file at <outputPath>-txid.
// Returns 0, nil if the file does not exist (first run).
func ReadTXIDFile(outputPath string) (ltx.TXID, error) {
txidPath := TXIDPath(outputPath)
data, err := os.ReadFile(txidPath)View on GitHub (pinned to 4ed7a308f6)
Solutions
- Check for file-descriptor exhaustion (ulimit -n) and raise it or fix descriptor leaks in the host process
- Inspect system logs for device writeback errors and repair the storage volume
- Remove stale '<outputPath>-txid.tmp' files and retry the operation
- Move the output path onto a local, POSIX-compliant filesystem
Example fix
// before: ignoring close errors in your own temp-file code
f.Close()
os.Rename(tmpPath, finalPath)
// after
if err := f.Close(); err != nil {
os.Remove(tmpPath)
return fmt.Errorf("close temp file: %w", err)
} Defensive patterns
Strategy: try-catch
Validate before calling
// Check descriptor headroom before batch operations
if rlim := getFDLimit(); rlim < 1024 { raiseFDLimitOrWarn() } Type guard
func fdBudgetOK() bool { return countOpenFDs() < fdLimit()-64 } Try / catch
if err := WriteTXIDFile(outputPath, txid); err != nil {
if errors.Is(err, syscall.EMFILE) {
// raise ulimit -n or fix fd leaks, then retry
}
return err
} Prevention
- Raise ulimit -n for long-running litestream processes
- Watch system logs for writeback/I/O errors
- Avoid SMB/network shares for database paths
- Retry the write; the temp file is auto-cleaned on failure
When it happens
Trigger: Calling WriteTXIDFile when closing '<outputPath>-txid.tmp' fails: delayed writeback errors surfacing at close, out-of-file-descriptors (EMFILE), or deferred f.Close() earlier in the function having already interacted oddly with the same descriptor on some platforms.
Common situations: File-descriptor exhaustion in long-running litestream processes; disk errors reported at close time after writeback; container limits (ulimit -n) too low; Windows/SMB shares returning errors on close.
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
- sync L0 file: %w
- sync txid file: %w
- sync txid dir: %w
- failed to scan directory %s: %w
- failed to calculate relative path for %s: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/f788fa3e37e6bddc.
Report an issue: GitHub.