{"record":{"id":"6bf0b791f8e69953","repo":"gastownhall/beads","slug":"read-rules-directory-w","errorCode":null,"errorMessage":"read rules directory: %w","messagePattern":"read rules directory: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"cmd/bd/rules.go","lineNumber":578,"sourceCode":"\tvar sourceNames []string\n\tfor _, r := range rules {\n\t\tsourceNames = append(sourceNames, r.Name+\".md\")\n\t}\n\tsb.WriteString(\"\\nSource rules: \")\n\tsb.WriteString(strings.Join(sourceNames, \", \"))\n\tsb.WriteString(\"\\n\")\n\n\treturn sb.String(), nil\n}\n\n// RunAudit is the top-level orchestrator for `bd rules audit`.\nfunc RunAudit(rulesDir string, threshold float64) (*AuditResult, error) {\n\tentries, err := os.ReadDir(rulesDir)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn &AuditResult{}, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"read rules directory: %w\", err)\n\t}\n\n\tvar rules []RuleFile\n\ttotalTokens := 0\n\n\tfor _, entry := range entries {\n\t\tif entry.IsDir() {\n\t\t\tcontinue\n\t\t}\n\t\tif !strings.HasSuffix(entry.Name(), \".md\") {\n\t\t\tcontinue\n\t\t}\n\n\t\tpath := filepath.Join(rulesDir, entry.Name())\n\t\trf, err := ParseRuleFile(path)\n\t\tif err != nil {\n\t\t\t// Skip files that can't be parsed\n\t\t\tfmt.Fprintf(os.Stderr, \"Warning: skipping %s: %v\\n\", entry.Name(), err)","sourceCodeStart":560,"sourceCodeEnd":596,"githubUrl":"https://github.com/gastownhall/beads/blob/71377f276968b452ee607177637970a4ff888584/cmd/bd/rules.go#L560-L596","documentation":"RunAudit wraps os.ReadDir failures for the rules directory. A missing directory is deliberately NOT an error (returns empty AuditResult); this error fires only for other ReadDir failures such as permission denied or the path being a file.","triggerScenarios":"RunAudit called with a rulesDir that exists but is unreadable (permissions) or is a regular file rather than a directory.","commonSituations":"Rules directory with restrictive permissions (e.g. created by another user or root-only); config pointing rulesDir at a file; mounted volume with wrong ownership; containerized environments with dropped capabilities.","solutions":["Check permissions: ls -ld <rulesDir>; ensure the running user has read+execute.","Verify the path is a directory, not a file: test -d <rulesDir>.","Fix ownership (chown/chmod) or point the rules dir config at a writable, readable location.","If IsNotExist, no error is thrown — check that you are not confusing this with a different failure."],"exampleFix":"// before\nres, err := RunAudit(rulesDir, 0.5)\n// after\nif info, statErr := os.Stat(rulesDir); statErr == nil && !info.IsDir() {\n    return fmt.Errorf(\"rules path %s is not a directory\", rulesDir)\n}\nres, err := RunAudit(rulesDir, 0.5)","handlingStrategy":"validation","validationCode":"if info, err := os.Stat(rulesDir); err != nil || !info.IsDir() {\n    return fmt.Errorf(\"rules dir %s is not a readable directory\", rulesDir)\n}","typeGuard":null,"tryCatchPattern":"res, err := RunAudit(rulesDir, threshold)\nif err != nil {\n    return fmt.Errorf(\"rules audit failed: %w\", err) // check permissions first\n}","preventionTips":["Ensure the rules dir is a directory, not a file.","chmod/chown the rules dir so the bd process user can read it.","Note that a missing dir returns an empty result, not this error — distinguish cases when debugging."],"tags":["filesystem","io","permissions","rules"],"backgroundTag":"permission-denied","analyzedSha":"71377f276968b452ee607177637970a4ff888584","analyzedAt":"2026-08-30T18:55:39.744Z","schemaVersion":2},"datasetVersion":"2026-08-30T23:17:21.991Z"}