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

  1. Find and wait for/kill the other chezmoi process holding the lock (lsof/fuser on the state file, typically ~/.config/chezmoi/chezmoistate.boltdb)
  2. Remove a stale lock by ensuring no chezmoi process is running, then retry
  3. Move the state file off network filesystems that lack flock support
  4. 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

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


AI-assisted analysis of twpayne/chezmoi@f901167e46 (2026-09-01). Data as JSON: /api/errors/bb1582cdbda1d471. Report an issue: GitHub.