{"record":{"id":"5a8fb4206755747e","repo":"gastownhall/beads","slug":"failed-to-open-s-securely-w","errorCode":null,"errorMessage":"failed to open %s securely: %w","messagePattern":"failed to open (.+?) securely: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/permissions.go","lineNumber":69,"sourceCode":"\t\tif os.IsNotExist(err) {\n\t\t\treturn false, nil // directory doesn't exist yet\n\t\t}\n\t\treturn false, fmt.Errorf(\"failed to inspect %s: %w\", path, err)\n\t}\n\tif info.Mode()&os.ModeSymlink != 0 {\n\t\treturn false, fmt.Errorf(\"refusing to chmod %s: path is a symbolic link\", path)\n\t}\n\tif !info.IsDir() {\n\t\treturn false, fmt.Errorf(\"refusing to chmod %s: path is not a directory\", path)\n\t}\n\tperm := info.Mode().Perm()\n\tif perm&0077 == 0 {\n\t\treturn false, nil // no group or world-accessible bits\n\t}\n\n\tdir, err := openDir(path)\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to open %s securely: %w\", path, err)\n\t}\n\tdefer func() { _ = dir.Close() }()\n\n\topenedInfo, err := dir.Stat()\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"failed to inspect opened directory %s: %w\", path, err)\n\t}\n\tif !openedInfo.IsDir() || !os.SameFile(info, openedInfo) {\n\t\treturn false, fmt.Errorf(\"refusing to chmod %s: path changed during permission repair\", path)\n\t}\n\tif err := dir.Chmod(BeadsDirPerm); err != nil {\n\t\treturn false, fmt.Errorf(\"failed to chmod %s to %04o: %w\", path, BeadsDirPerm, err)\n\t}\n\treturn true, nil\n}\n","sourceCodeStart":51,"sourceCodeEnd":85,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/config/permissions.go#L51-L85","documentation":"After lstat checks pass, fixBeadsDirPermissions opens the directory via a secure directory handle (openBeadsDirHandle, O_DIRECTORY/no-follow based) so the subsequent chmod operates on the opened handle rather than the path. This error wraps any failure from that secure open, such as openDir returning ENOENT, ELOOP, ENOTDIR, or permission denial on open.","triggerScenarios":"Calling FixBeadsDirPermissions on a path whose group/world bits are set, and the secure open of the directory fails — e.g. the directory was deleted or replaced between Lstat and open, the open syscall flags are unsupported on the platform/filesystem, or the process lacks search permission on a parent directory.","commonSituations":"Race where the directory is removed while repairing; network/overlay filesystems that do not support O_DIRECTORY or no-follow opens; running without execute permission on a parent directory; sandboxed environments restricting openat flags.","solutions":["Re-run the operation — transient races between Lstat and open usually resolve on retry.","Confirm the path is still a real directory: ls -ld <path>.","Check parent-directory execute permissions (chmod +x on ancestors as needed).","If the filesystem (e.g. some FUSE/network mounts) does not support secure directory opens, chmod the directory manually: chmod 700 <path>."],"exampleFix":"// fallback when secure open is unsupported\nchanged, err := config.FixBeadsDirPermissions(beadsDir)\nif err != nil {\n    _ = os.Chmod(beadsDir, 0700) // manual fallback\n}","handlingStrategy":"retry","validationCode":"info, err := os.Lstat(beadsDir)\nif err != nil || !info.IsDir() {\n    return fmt.Errorf(\"%s is not a usable directory\", beadsDir)\n}\n// check ancestors are searchable\nfor dir := filepath.Dir(beadsDir); ; dir = filepath.Dir(dir) {\n    if st, err := os.Stat(dir); err != nil || st.Mode().Perm()&0001 == 0 {\n        return fmt.Errorf(\"cannot traverse %s\", dir)\n    }\n    if dir == \"/\" { break }\n}","typeGuard":null,"tryCatchPattern":"changed, err := config.FixBeadsDirPermissions(beadsDir)\nif err != nil {\n    if strings.Contains(err.Error(), \"failed to open\") {\n        time.Sleep(50 * time.Millisecond)\n        changed, err = config.FixBeadsDirPermissions(beadsDir) // one retry for races\n    }\n    if err != nil {\n        log.Printf(\"permission fix failed, chmod manually: %v\", err)\n    }\n}","preventionTips":["Don't delete/recreate .beads while bd commands run.","Avoid placing .beads on FUSE/network filesystems with limited openat support.","Keep parent directories executable (0700/0755)."],"tags":["permissions","filesystem","syscall","security"],"backgroundTag":"secure-open-failed","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}