benbjohnson/litestream · error
read txid file: %w
Error message
read txid file: %w
What it means
ReadTXIDFile reads the '<outputPath>-txid' sidecar file to recover the last applied TXID. A missing file is treated as first-run (returns 0, nil), but any other read error is wrapped as 'read txid file: %w'. This indicates the TXID file exists but could not be read, so the replica cannot determine its resume point.
Source
Thrown at replica.go:1762
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)
if os.IsNotExist(err) {
return 0, nil
} else if err != nil {
return 0, fmt.Errorf("read txid file: %w", err)
}
txid, err := ltx.ParseTXID(strings.TrimSpace(string(data)))
if err != nil {
return 0, fmt.Errorf("parse txid file: %w", err)
}
return txid, nil
}
// ValidationError represents a single validation issue.
type ValidationError struct {
Level int // compaction level
Type string // "gap", "overlap", or "unsorted"
Message string // human-readable description
PrevFile *ltx.FileInfo // previous file
CurrFile *ltx.FileInfo // current file that caused error
}View on GitHub (pinned to 4ed7a308f6)
Solutions
- Fix permissions on the '<outputPath>-txid' file so the litestream user can read it (chown/chmod)
- If the path is a directory or corrupted beyond repair, remove it and let litestream re-create it via a fresh restore/sync
- Run litestream as the same user that owns the database and sidecar files
- Check device health if dmesg shows I/O errors on the volume
Example fix
# before -rw------- root root my.db-txid # litestream runs as litestream user # after chown litestream:litestream my.db-txid && chmod 644 my.db-txid
Defensive patterns
Strategy: validation
Validate before calling
txidPath := TXIDPath(outputPath)
if st, err := os.Stat(txidPath); err == nil && st.IsDir() {
return fmt.Errorf("%s is a directory; remove it", txidPath)
}
if info, err := os.Stat(txidPath); err == nil {
f, _ := os.Open(txidPath)
defer f.Close()
if _, err := f.Read(make([]byte, 1)); err != nil {
return fmt.Errorf("txid file unreadable: %w", err)
}
} Type guard
func txidFileReadable(path string) bool {
f, err := os.Open(path)
if err != nil { return false }
defer f.Close()
buf := make([]byte, 1)
_, err = f.Read(buf)
return err == nil
} Try / catch
txid, err := ReadTXIDFile(outputPath)
if err != nil {
if errors.Is(err, os.ErrPermission) {
// fix ownership/permissions or run as file owner, then retry
}
return err
} Prevention
- Run litestream as the same user that owns the database files
- Never create files/dirs named '<db>-txid' manually
- Standardize the litestream service user across deployments
- Check permissions after restores performed as root
When it happens
Trigger: os.ReadFile on '<outputPath>-txid' failing with EACCES (wrong permissions/owner), EISDIR (a directory occupies the path), or device I/O errors — while the file (or same-named directory) exists.
Common situations: TXID file created by root but litestream running as another user; leftover directory named 'my.db-txid' from a misconfiguration; corrupted storage; changed umask or container user after an upgrade.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- remove L0 directory: %w
- recreate L0 directory: %w
- failed to scan directory %s: %w
- failed to calculate relative path for %s: %w
- cannot access output path: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/fd624a6aaf5ec073.
Report an issue: GitHub.