charmbracelet/crush · error
open lock file %q: %w
Error message
open lock file %q: %w
What it means
lock.File opens (creating if necessary) the lock file with os.OpenFile(O_RDWR|O_CREATE, 0600) before attempting to flock it. This error wraps a failure of that open, meaning the lock could not even be created or opened for reading/writing. It is returned before any locking is attempted.
Source
Thrown at internal/lock/lock.go:41
"errors"
"fmt"
"os"
)
// ErrContended is returned by TryFile when the lock is already held by
// another process.
var ErrContended = errors.New("file lock is held by another process")
// File acquires an exclusive advisory lock on the file at path, blocking
// until the lock is acquired or ctx is cancelled. It returns a release
// function that drops the lock and closes the underlying file descriptor.
//
// Pass a context with a deadline (e.g. context.WithTimeout) to bound the
// wait. Pass context.Background() to block indefinitely.
func File(ctx context.Context, path string) (func(), error) {
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0o600)
if err != nil {
return nil, fmt.Errorf("open lock file %q: %w", path, err)
}
release, err := lockFile(ctx, f)
if err != nil {
f.Close()
return nil, err
}
return func() {
release()
f.Close()
}, nil
}
// TryFile is like File but returns ErrContended immediately if the lock
// is already held by another process. Use this when you want to fail
// fast rather than wait.
func TryFile(path string) (func(), error) {View on GitHub (pinned to 7944b8e522)
Solutions
- Ensure the parent directory of the lock path exists before calling lock.File (os.MkdirAll on the data dir).
- Check ownership/permissions of the existing lock file (0600 owned by the same user) and remove a stale file with wrong ownership.
- Verify path points to a file, not a directory, and the filesystem is writable.
- If a stale lock from a crashed process has wrong permissions, delete it: rm <path> and retry.
Example fix
// before
release, err := lock.File(ctx, filepath.Join(dataDir, "crush.lock"))
// after
if err := os.MkdirAll(dataDir, 0o755); err != nil {
return fmt.Errorf("prepare data dir: %w", err)
}
release, err := lock.File(ctx, filepath.Join(dataDir, "crush.lock"))
if err != nil {
return fmt.Errorf("acquire data-dir lock: %w", err)
} Defensive patterns
Strategy: validation
Validate before calling
lockDir := filepath.Dir(path)
if info, err := os.Stat(path); err == nil && info.IsDir() {
return fmt.Errorf("%s is a directory, expected lock file", path)
}
if err := os.MkdirAll(lockDir, 0o755); err != nil {
return fmt.Errorf("cannot create lock dir: %w", err)
} Try / catch
release, err := lock.File(ctx, path)
if err != nil {
var perr *fs.PathError
if errors.As(err, &perr) {
return fmt.Errorf("lock open failed (%s): %w", perr.Op, err)
}
return err
} Prevention
- Always create the data directory before attempting to lock.
- Run the app as the same user that owns the data directory.
- Avoid placing lock files on read-only or network filesystems.
When it happens
Trigger: Calling lock.File(ctx, path) where the parent directory of path does not exist, the path is a directory, or the process lacks write permission on an existing lock file or its directory.
Common situations: Data directory deleted while the app runs or never created before locking; running with a different (unprivileged) user than the one that created the lock file; a directory placed where the lock file is expected; read-only filesystem.
Related errors
- failed to create parent directories: %w
- failed to create output file: %w
- failed to access file: %w
- failed to create parent directories: %w
- session ID is required for accessing directories outside wor
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/a6340d9e1a9a11c9.
Report an issue: GitHub.