gastownhall/beads · error
creating beads directory: %w
Error message
creating beads directory: %w
What it means
AcquireSyncLock ensures the beads directory exists before creating the lock file, calling os.MkdirAll(beadsDir, 0755). If directory creation fails it wraps the OS error as "creating beads directory: %w" and returns no lock. This typically means a filesystem-level problem, since MkdirAll succeeds silently when the directory already exists.
Source
Thrown at internal/linear/synclock.go:39
file *os.File
metadata syncLockMetadata
}
// SyncLockInfo contains advisory metadata published while the sync lock is held.
type SyncLockInfo struct {
PID int
Started time.Time
}
// AcquireSyncLock acquires the sync lock for the given beads directory.
// If wait is true, blocks until the lock is available. If false, returns
// an error immediately when the lock is held by another live process.
func AcquireSyncLock(beadsDir string, wait bool) (*SyncLock, error) {
lockPath := filepath.Join(beadsDir, syncLockFilename)
infoPath := syncLockMetadataPath(beadsDir, lockPath)
if err := os.MkdirAll(beadsDir, 0755); err != nil {
return nil, fmt.Errorf("creating beads directory: %w", err)
}
f, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0600) // #nosec G304 -- lockPath is constrained to the beads directory.
if err != nil {
return nil, fmt.Errorf("opening lock file: %w", err)
}
if wait {
if err := lockfile.FlockExclusiveBlocking(f); err != nil {
_ = f.Close()
return nil, fmt.Errorf("acquiring lock (blocking): %w", err)
}
} else {
if err := lockfile.FlockExclusiveNonBlocking(f); err != nil {
if lockfile.IsLocked(err) || err == lockfile.ErrLockBusy {
info := readContendedSyncLockInfo(infoPath)
_ = f.Close()
return nil, &SyncLockHeldError{Info: info}View on GitHub (pinned to 71377f2769)
Solutions
- Check that no regular file exists at the beadsDir path (or along it); rename or remove it
- Verify write permission on the parent directory for the running user
- Confirm the filesystem is writable (not mounted read-only, disk not full)
- Validate the beadsDir string for typos or invalid characters before calling AcquireSyncLock
Example fix
// before: assume dir creatable
lock, err := AcquireSyncLock(beadsDir, true)
// after: pre-flight
if st, ferr := os.Stat(beadsDir); ferr == nil && !st.IsDir() {
return fmt.Errorf("%s exists and is not a directory", beadsDir)
} Defensive patterns
Strategy: validation
Validate before calling
if st, err := os.Stat(beadsDir); err == nil && !st.IsDir() {
return fmt.Errorf("%s exists and is not a directory", beadsDir)
}
lock, err := AcquireSyncLock(beadsDir, true) Type guard
null
Try / catch
lock, err := AcquireSyncLock(beadsDir, true)
if err != nil && strings.Contains(err.Error(), "creating beads directory") {
return fmt.Errorf("fix filesystem access to %s: %w", beadsDir, err)
} Prevention
- Ensure no regular file occupies the beadsDir path
- Provision the .beads directory with correct ownership before running sync
- Avoid placing .beads on read-only or network volumes
When it happens
Trigger: os.MkdirAll(beadsDir, 0755) returns an error: parent path component is a file, permission denied on the parent, invalid path characters, or the filesystem is read-only.
Common situations: beadsDir points inside a read-only mount or container layer; a file exists where a directory is expected (e.g. .beads is a file); wrong user/permissions in multi-user setups; USB/network volume disconnected.
Related errors
- opening lock file: %w
- util: creating lock directory: %w
- util: opening lock file: %w
- dolt path is not executable
- failed to create backup directory: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/d60eb3f8f78a12e2.
Report an issue: GitHub.