{"record":{"id":"8340e59c4a7c33ed","repo":"gastownhall/beads","slug":"fs-beadsdirexists-stat-s-w","errorCode":null,"errorMessage":"fs: BeadsDirExists: stat %s: %w","messagePattern":"fs: BeadsDirExists: stat (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/storage/domain/fs/beads.go","lineNumber":82,"sourceCode":"\tif r.beadsDir == \"\" {\n\t\treturn fmt.Errorf(\"fs: CreateBeadsDir: beadsDir not resolved\")\n\t}\n\tif err := os.MkdirAll(r.beadsDir, config.BeadsDirPerm); err != nil {\n\t\treturn fmt.Errorf(\"fs: CreateBeadsDir: mkdir %s: %w\", r.beadsDir, err)\n\t}\n\tif _, err := config.FixBeadsDirPermissions(r.beadsDir); err != nil {\n\t\treturn fmt.Errorf(\"fs: CreateBeadsDir: fix perms %s: %w\", r.beadsDir, err)\n\t}\n\treturn nil\n}\n\nfunc (r *beadsDirFSRepositoryImpl) BeadsDirExists(ctx context.Context) (bool, error) {\n\tinfo, err := os.Stat(r.beadsDir)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\treturn false, nil\n\t}\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"fs: BeadsDirExists: stat %s: %w\", r.beadsDir, err)\n\t}\n\treturn info.IsDir(), nil\n}\n\nfunc (r *beadsDirFSRepositoryImpl) WriteBeadsGitignore(ctx context.Context) error {\n\tif r.templates.BeadsGitignore == \"\" {\n\t\treturn fmt.Errorf(\"fs: WriteBeadsGitignore: template not configured\")\n\t}\n\tpath := filepath.Join(r.beadsDir, \".gitignore\")\n\t// #nosec G304 -- path joined under bound beadsDir\n\texisting, err := os.ReadFile(path)\n\tif errors.Is(err, os.ErrNotExist) {\n\t\tif werr := os.WriteFile(path, []byte(r.templates.BeadsGitignore), 0600); werr != nil {\n\t\t\treturn fmt.Errorf(\"fs: WriteBeadsGitignore: %w\", werr)\n\t\t}\n\t\treturn nil\n\t}\n\tif err != nil {","sourceCodeStart":64,"sourceCodeEnd":100,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/internal/storage/domain/fs/beads.go#L64-L100","documentation":"BeadsDirExists reports whether the .beads directory exists. This error is returned when os.Stat on beadsDir fails with anything OTHER than os.ErrNotExist (which is treated as 'does not exist', not an error). It means existence could not be determined at all.","triggerScenarios":"Calling BeadsDirExists when os.Stat(r.beadsDir) fails with e.g. EACCES on a parent directory, ELOOP from a symlink cycle, ENAMETOOLONG, or I/O errors — any stat error except ErrNotExist.","commonSituations":"A parent directory in the path lacks execute permission for the current user; beadsDir contains a symlink loop; overly long path on constrained filesystems; network filesystem temporarily unavailable.","solutions":["Fix the stat failure indicated by the wrapped cause — typically chmod +x the parent directories so the path becomes traversable","Remove or repair broken/cyclic symlinks along the beadsDir path","Shorten the path if ENAMETOOLONG (move the project out of deeply nested dirs)","Retry if the cause is a transient network-filesystem error"],"exampleFix":"// before\n$ ls -ld ~/project  # drwx------ root\n$ bd ready          // stat ~/project/.beads: permission denied\n// after\n$ sudo chmod o+x ~/project\n$ bd ready  # existence check succeeds","handlingStrategy":"type-guard","validationCode":"func beadsDirCheckable(path string) error {\n\tfor dir := path; ; dir = filepath.Dir(dir) {\n\t\tif _, err := os.Stat(dir); err != nil {\n\t\t\tif !errors.Is(err, os.ErrNotExist) {\n\t\t\t\treturn fmt.Errorf(\"cannot traverse %s: %v\", dir, err)\n\t\t\t}\n\t\t}\n\t\tif dir == filepath.Dir(dir) { return nil }\n\t}\n}\nif err := beadsDirCheckable(beadsDir); err != nil { return err }\nexists, err := repo.BeadsDirExists(ctx)","typeGuard":"func isStatErr(err error) bool {\n\tvar pe *fs.PathError\n\treturn errors.As(err, &pe) && pe.Op == \"stat\"\n}","tryCatchPattern":"exists, err := repo.BeadsDirExists(ctx)\nif err != nil {\n\tif isStatErr(err) && errors.Is(errors.Unwrap(err), syscall.EACCES) {\n\t\treturn fmt.Errorf(\"fix parent-directory execute permission first\")\n\t}\n\treturn err\n}","preventionTips":["Keep paths short and symlink-free between the repo root and .beads","Ensure every ancestor directory has the execute bit for the running user","Watch for path-length limits when nesting deeply on constrained filesystems","Do not race bd with tools that rename/remove parent directories"],"tags":["filesystem","stat","permissions"],"backgroundTag":"path-traversal-permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}