twpayne/chezmoi · error
open %s: %w
Error message
open %s: %w
What it means
chezmoi's persistent state is a bbolt database file. When bbolt.Open fails with EINVAL, the code assumes flock(2) failed (the file is already locked by another process); other errors are wrapped with the state file path so the developer knows which file could not be opened. This prevents silent corruption from two processes writing the state concurrently.
Source
Thrown at internal/chezmoi/boltpersistentstate.go:238
}
return b.Put(key, value)
})
}
// open opens b's database if it is not already open, creating it if needed.
func (b *BoltPersistentState) open() error {
if b.db != nil {
return nil
}
if err := MkdirAll(b.system, b.path.Dir(), fs.ModePerm); err != nil {
return err
}
switch db, err := bbolt.Open(b.path.String(), 0o600, &b.options); {
case errors.Is(err, syscall.EINVAL):
// Assume that any EINVAL error is because flock(2) failed.
return fmt.Errorf("open %s: failed to acquire lock: %w", b.path, err)
case err != nil:
return fmt.Errorf("open %s: %w", b.path, err)
default:
b.empty = false
b.db = db
return nil
}
}
View on GitHub (pinned to f901167e46)
Solutions
- Find and wait for/kill the other chezmoi process holding the lock (lsof/fuser on the state file, typically ~/.config/chezmoi/chezmoistate.boltdb)
- Remove a stale lock by ensuring no chezmoi process is running, then retry
- Move the state file off network filesystems that lack flock support
- If the file is corrupt and no process holds it, back it up and delete it so chezmoi recreates it
Example fix
// before: parallel runs collide chezmoi apply & chezmoi apply & // after: serialize runs flock /tmp/chezmoi.lock -c 'chezmoi apply'
Defensive patterns
Strategy: retry
Validate before calling
// before operating on state, check no other holder
out, _ := exec.Command("fuser", statePath.String()).Output()
if len(bytes.TrimSpace(out)) > 0 { /* another process holds the lock */ } Try / catch
if err := ps.Delete(key); err != nil {
if strings.Contains(err.Error(), "failed to acquire lock") {
time.Sleep(retryBackoff) // then retry
}
} Prevention
- Never run two chezmoi commands concurrently; use flock or a job scheduler to serialize
- Check for stuck chezmoi processes with pgrep before long operations
- Keep the state file on a local filesystem with flock support
When it happens
Trigger: Calling CopyTo, Data, Delete, DeleteBucket, ForEach, or Get on BoltPersistentState while another chezmoi process (or a stale process) holds the flock on the state file, or when the file/disk rejects the open (e.g. bad path on unsupported filesystem).
Common situations: Two chezmoi runs in parallel (cron + manual run, IDE integration + shell), a hung previous run still holding the lock, state file on a network filesystem that does not support flock, or a corrupted/unwritable state path.
Related errors
- %s: unknown compression format
- %s: parse error
- %s: unknown entry type
- expected a []string, got a %T
- expected a []string, got a %T element
AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01).
Data as JSON: /api/errors/bb1582cdbda1d471.
Report an issue: GitHub.