gastownhall/beads · error
util: creating lock directory: %w
Error message
util: creating lock directory: %w
What it means
TryLock ensures the parent directory of the lock path exists (mode 0700) before creating the lock file; this error wraps an os.MkdirAll failure. It means the lock could not even be attempted because the containing directory could not be created.
Source
Thrown at internal/storage/dbproxy/util/flock.go:29
// Unlocker is the interface for releasing an acquired lock.
type Unlocker interface {
Unlock()
}
// Lock holds an exclusive flock on a file.
type Lock struct {
f *os.File
}
// TryLock attempts to acquire a non-blocking exclusive flock on lockPath.
// The parent directory is created (mode 0700) if it does not exist. On
// contention returns an error that satisfies lockfile.IsLocked, so callers
// can detect "another holder is alive" and produce their own contextual
// error message.
func TryLock(lockPath string) (*Lock, error) {
if err := os.MkdirAll(filepath.Dir(lockPath), 0700); err != nil {
return nil, fmt.Errorf("util: creating lock directory: %w", err)
}
f, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0600) //nolint:gosec // lockPath comes from caller-derived dirs, not user input
if err != nil {
return nil, fmt.Errorf("util: opening lock file: %w", err)
}
if err := lockfile.FlockExclusiveNonBlocking(f); err != nil {
_ = f.Close()
return nil, err
}
return &Lock{f: f}, nil
}
// File returns the underlying *os.File. Useful for fork+exec lock-fd
// inheritance: pass it via cmd.ExtraFiles, then Close() the parent's fd —
// the child retains the lock through its inherited fd, which references the
// same open file description.
func (l *Lock) File() *os.File {
return l.fView on GitHub (pinned to 71377f2769)
Solutions
- Check whether a component of the lock path is an existing file: ls -la each ancestor directory and remove/rename the offending file
- Fix permissions: chown/chmod the ancestor directories (0700 is required to be creatable by the current user)
- Ensure the filesystem is writable (mount rw, not read-only container root)
- Validate the lockPath configuration value points inside your data directory
Example fix
// before
lock, err := util.TryLock("/var/lib/beads/db.lock") // /var/lib is root-owned
// after
os.MkdirAll("/var/lib/beads", 0o700) // or choose a dir the current user owns
lock, err := util.TryLock("/var/lib/beads/db.lock") Defensive patterns
Strategy: validation
Validate before calling
dir := filepath.Dir(lockPath)
if st, err := os.Stat(dir); err == nil && !st.IsDir() {
return fmt.Errorf("%s exists and is not a directory", dir)
}
if err := syscall.Access(filepath.Dir(dir), syscall.W_OK); err != nil {
return fmt.Errorf("cannot create %s: %w", dir, err)
} Try / catch
lock, err := util.TryLock(lockPath)
if err != nil {
if errors.Is(err, fs.ErrPermission) {
return fmt.Errorf("lock dir %s not writable by %s: %w", filepath.Dir(lockPath), os.Getuid(), err)
}
return err
}
defer lock.Close() Prevention
- Derive lockPath from the same configurable data directory used by the database
- Ensure the service runs as a consistent user across restarts (avoid root-created dirs)
- Mount data volumes read-write; never place lock files on tmpfs that gets wiped
When it happens
Trigger: Calling util.TryLock(lockPath) where filepath.Dir(lockPath) cannot be created — a path component is a regular file, permission denied on an ancestor, or the path is on a read-only filesystem.
Common situations: Lock path misconfigured (e.g. .beads path colliding with an existing file); running under a different UID than the database directory owner; read-only root filesystem in containers; disk full preventing mkdir.
Related errors
- util: opening lock file: %w
- dolt path is not executable
- failed to create backup directory: %w
- failed to create temp file: %w
- failed to create backup file: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/f0f8bb826237e66c.
Report an issue: GitHub.