benbjohnson/litestream · error
remove ltx directory: %w
Error message
remove ltx directory: %w
What it means
Raised during local state reset (litestream reset / DB reset path) when os.RemoveAll on the database's LTX directory fails with an error other than not-exist. The wrapper is 'remove ltx directory: %w'. Reset aborts so a half-deleted local state is never silently reused.
Source
Thrown at db.go:532
db.metaPath = path
}
// LTXDir returns path of the root LTX directory.
func (db *DB) LTXDir() string {
return filepath.Join(db.metaPath, "ltx")
}
// ResetLocalState removes local LTX files, forcing a fresh snapshot on next sync.
// This is useful for recovering from corrupted or missing LTX files.
// The database file itself is not modified.
func (db *DB) ResetLocalState(ctx context.Context) error {
db.Logger.Info("resetting local litestream state",
"meta_path", db.metaPath,
"ltx_dir", db.LTXDir())
// Remove all LTX files
if err := os.RemoveAll(db.LTXDir()); err != nil && !os.IsNotExist(err) {
return fmt.Errorf("remove ltx directory: %w", err)
}
// Clear cached LTX file info
db.maxLTXFileInfos.Lock()
db.maxLTXFileInfos.m = make(map[int]*ltx.FileInfo)
db.maxLTXFileInfos.Unlock()
db.invalidatePosCache()
db.Logger.Info("local state reset complete, next sync will create fresh snapshot")
return nil
}
// LTXLevelDir returns path of the given LTX compaction level.
// Panics if level is negative.
func (db *DB) LTXLevelDir(level int) string {
return filepath.Join(db.LTXDir(), strconv.Itoa(level))
}View on GitHub (pinned to 4ed7a308f6)
Solutions
- Stop the litestream process before resetting so no files are held open
- Fix permissions/ownership on the LTX directory (chown/chmod) or run reset as the same user as litestream
- Ensure the volume is writable (not read-only mount)
- Remove the directory manually as root, then retry
- Confirm the path from 'meta_path'/'ltx_dir' in the preceding log line points where you expect
Example fix
// before: reset while daemon holds handles $ litestream reset /var/lib/db // remove ltx directory: unlinkat /var/lib/db/ltx: device or resource busy // after: stop first, then reset $ systemctl stop litestream $ litestream reset /var/lib/db $ systemctl start litestream
Defensive patterns
Strategy: validation
Validate before calling
// Check writability and that no process holds the dir before reset
test := filepath.Join(ltxDir, ".probe")
if err := os.WriteFile(test, nil, 0o600); err != nil {
return fmt.Errorf("ltx dir not writable: %w", err)
}
os.Remove(test)
// also: pgrep litestream must return nothing Try / catch
if err := resetDB(dbPath); err != nil {
if errors.Is(err, os.ErrPermission) {
// advise running as the litestream user
}
return err
} Prevention
- Stop the litestream daemon before resetting
- Run reset as the same OS user that owns the meta/LTX dirs
- Use writable volumes for the litestream data path
When it happens
Trigger: Calling the DB reset path (db.go reset) while db.LTXDir() cannot be removed — filesystem permission denied, directory busy (open file handles by a running litestream process), read-only mount, or I/O error.
Common situations: Running reset as a user without ownership of /var/lib/litestream; litestream daemon still running and holding files open; container with read-only data volume; NFS/ephemeral storage I/O failures.
Related errors
- cannot access output path: %w
- cannot access SQLite sidecar path: %w
- remove existing output path: %w
- stat database: %w
- create parent directory: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/2bf15653e93c4480.
Report an issue: GitHub.