gastownhall/beads · error
read rule file %s: %w
Error message
read rule file %s: %w
What it means
ParseRuleFile wraps any os.ReadFile failure with the path of the .md rule file it attempted to read. It is thrown when the file cannot be opened or read (missing file, permission denied, path is a directory). The %w wrap preserves the underlying os error for errors.Is/As inspection.
Source
Thrown at cmd/bd/rules.go:104
"suppress": {"log"},
}
// --- Regex Patterns ---
var (
headingRe = regexp.MustCompile(`(?m)^#\s+(.+)`)
doRe = regexp.MustCompile(`(?i)^\*\*Do:?\*\*:?\s*(.*)`)
dontRe = regexp.MustCompile(`(?i)^\*\*Don'?t:?\*\*:?\s*(.*)`)
)
// --- Core Functions ---
// ParseRuleFile reads a .md file and extracts structured rule data.
func ParseRuleFile(path string) (RuleFile, error) {
// #nosec G304 -- path comes from controlled filepath.Join of user-specified rules directory
data, err := os.ReadFile(path)
if err != nil {
return RuleFile{}, fmt.Errorf("read rule file %s: %w", path, err)
}
content := string(data)
name := strings.TrimSuffix(filepath.Base(path), ".md")
rf := RuleFile{
Path: path,
Name: name,
Body: content,
// Rough token estimate: 1 token ~ 4 chars
Tokens: len(content) / 4,
}
// Extract title from first heading
if m := headingRe.FindStringSubmatch(content); len(m) > 1 {
rf.Title = strings.TrimSpace(m[1])
} else {
rf.Title = nameView on GitHub (pinned to 71377f2769)
Solutions
- Verify the file exists and is readable: ls -l <path> (or stat <path>).
- Check that the rules directory configured for bd contains the expected .md files.
- Fix filesystem permissions on the rules directory/file.
- If the path should be optional, handle the wrapped os.IsNotExist(err) before treating it as fatal.
Example fix
// before
rf, err := ParseRuleFile(maybePath)
if err != nil { return err }
// after
if _, statErr := os.Stat(maybePath); os.IsNotExist(statErr) {
return nil // skip missing rule file
}
rf, err := ParseRuleFile(maybePath)
if err != nil { return err } Defensive patterns
Strategy: try-catch
Validate before calling
if _, err := os.Stat(path); err != nil {
// skip or report before calling ParseRuleFile
} Try / catch
rf, err := ParseRuleFile(path)
if err != nil {
if errors.Is(err, os.ErrNotExist) { /* handle missing file */ }
return fmt.Errorf("rules audit: %w", err)
} Prevention
- Stat the file before parsing when existence is optional.
- Keep rules .md files under a single versioned directory so paths are stable.
- Run bd as a user with read access to the rules directory.
- Never pass directory paths to ParseRuleFile.
When it happens
Trigger: Calling ParseRuleFile (directly or via RunAudit/runRulesCompact) with a path that does not exist, is unreadable, or points to a directory instead of a .md file.
Common situations: Misconfigured rules directory passed to `bd rules audit`/`bd rules compact`; a rules file was renamed or deleted; symlink pointing at a missing target; filesystem permission issues; passing a directory path instead of a file.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- read rules directory: %w
- failed to read backup state: %w
- failed to write temp file: %w
- failed to sync temp file: %w
- resolve proxied server root: %w
AI-assisted analysis of gastownhall/beads@71377f2769 (2026-08-30).
Data as JSON: /api/errors/16e432500e6d59fd.
Report an issue: GitHub.