{"record":{"id":"d60eb3f8f78a12e2","repo":"gastownhall/beads","slug":"creating-beads-directory-w","errorCode":null,"errorMessage":"creating beads directory: %w","messagePattern":"creating beads directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/linear/synclock.go","lineNumber":39,"sourceCode":"\tfile     *os.File\n\tmetadata syncLockMetadata\n}\n\n// SyncLockInfo contains advisory metadata published while the sync lock is held.\ntype SyncLockInfo struct {\n\tPID     int\n\tStarted time.Time\n}\n\n// AcquireSyncLock acquires the sync lock for the given beads directory.\n// If wait is true, blocks until the lock is available. If false, returns\n// an error immediately when the lock is held by another live process.\nfunc AcquireSyncLock(beadsDir string, wait bool) (*SyncLock, error) {\n\tlockPath := filepath.Join(beadsDir, syncLockFilename)\n\tinfoPath := syncLockMetadataPath(beadsDir, lockPath)\n\n\tif err := os.MkdirAll(beadsDir, 0755); err != nil {\n\t\treturn nil, fmt.Errorf(\"creating beads directory: %w\", err)\n\t}\n\n\tf, err := os.OpenFile(lockPath, os.O_CREATE|os.O_RDWR, 0600) // #nosec G304 -- lockPath is constrained to the beads directory.\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"opening lock file: %w\", err)\n\t}\n\n\tif wait {\n\t\tif err := lockfile.FlockExclusiveBlocking(f); err != nil {\n\t\t\t_ = f.Close()\n\t\t\treturn nil, fmt.Errorf(\"acquiring lock (blocking): %w\", err)\n\t\t}\n\t} else {\n\t\tif err := lockfile.FlockExclusiveNonBlocking(f); err != nil {\n\t\t\tif lockfile.IsLocked(err) || err == lockfile.ErrLockBusy {\n\t\t\t\tinfo := readContendedSyncLockInfo(infoPath)\n\t\t\t\t_ = f.Close()\n\t\t\t\treturn nil, &SyncLockHeldError{Info: info}","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/linear/synclock.go#L21-L57","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: assume dir creatable\nlock, err := AcquireSyncLock(beadsDir, true)\n// after: pre-flight\nif st, ferr := os.Stat(beadsDir); ferr == nil && !st.IsDir() {\n    return fmt.Errorf(\"%s exists and is not a directory\", beadsDir)\n}","handlingStrategy":"validation","validationCode":"if st, err := os.Stat(beadsDir); err == nil && !st.IsDir() {\n    return fmt.Errorf(\"%s exists and is not a directory\", beadsDir)\n}\nlock, err := AcquireSyncLock(beadsDir, true)","typeGuard":"null","tryCatchPattern":"lock, err := AcquireSyncLock(beadsDir, true)\nif err != nil && strings.Contains(err.Error(), \"creating beads directory\") {\n    return fmt.Errorf(\"fix filesystem access to %s: %w\", beadsDir, err)\n}","preventionTips":["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"],"tags":["filesystem","permissions","locking"],"backgroundTag":"mkdir-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}