alibaba/open-code-review · error
read rule file %s: %w
Error message
read rule file %s: %w
What it means
loadRuleFile reads a project rule file at a given path (e.g. <repo>/.opencodereview/rule.json). Unlike the global loader, a missing file here is also surfaced: any os.ReadFile error — including fs.ErrNotExist — is wrapped with the path. This tells you exactly which config file could not be opened.
Source
Thrown at internal/config/rules/system_rules.go:396
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil, nil
}
return nil, fmt.Errorf("read global rule %s: %w", path, err)
}
var pr ProjectRule
if err := json.Unmarshal(data, &pr); err != nil {
return nil, fmt.Errorf("unmarshal global rule: %w", err)
}
resolveRuleEntries(pr.Rules, filepath.Dir(path), "")
return &pr, nil
}
func loadRuleFile(path string) (*ProjectRule, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read rule file %s: %w", path, err)
}
var pr ProjectRule
if err := json.Unmarshal(data, &pr); err != nil {
return nil, fmt.Errorf("unmarshal rule file %s: %w", path, err)
}
resolveRuleEntries(pr.Rules, filepath.Dir(path), "")
return &pr, nil
}
// loadProjectRule reads <repoDir>/.opencodereview/rule.json. Since #287 anchored
// RepoDir at the git top-level, `ocr review` from a monorepo subdirectory loads
// the repo-root rule file — which is consistent, since rule entries match against
// root-relative diff paths. A subproject-local rule.json under the subdirectory is
// intentionally not consulted; put shared rules at the repo root, or pass --rule.
func loadProjectRule(repoDir string) (*ProjectRule, error) {
confineRoot, err := pathutil.CanonicalPath(repoDir)
if err != nil {
return nil, fmt.Errorf("resolve repo dir %s: %w", repoDir, err)View on GitHub (pinned to 5cf97d0d15)
Solutions
- Check the path from the error message: ls -la <repoDir>/.opencodereview/ and create the rule.json if absent
- Verify you're running from the repo the config belongs to (the path is anchored to the repo directory since #287)
- Fix permissions (chmod u+r) or move the file out of a directory the user cannot read
Example fix
// before: file missing
// err: read rule file /repo/.opencodereview/rule.json: open ...: no such file or directory
// after
$ mkdir -p .opencodereview && printf '{"rules": []}' > .opencodereview/rule.json Defensive patterns
Strategy: validation
Validate before calling
p := filepath.Join(repoDir, ".opencodereview", "rule.json")
if _, err := os.Stat(p); errors.Is(err, os.ErrNotExist) {
return fmt.Errorf("project rule file missing: %s (create it or unset the project-rule path)", p)
} Try / catch
pr, err := loadRuleFile(path)
if err != nil {
if errors.Is(err, os.ErrNotExist) { // errors.Is works through the %w wrap
log.Printf("no project rule file at %s; using defaults", path)
return nil, nil
}
return err
} Prevention
- Commit .opencodereview/rule.json (or document that it's local-only and unset it otherwise)
- Run the CLI from the repository root so the anchored path resolves
- After cloning or copying a repo, verify the file survived (it may be gitignored)
When it happens
Trigger: os.ReadFile(path) fails: the project rule file does not exist at the configured path, a parent directory is missing, permissions deny read, or the path is a directory.
Common situations: Repository configured to use .opencodereview/rule.json that was never committed or is gitignored; running the tool outside the repo root so the relative anchor differs; restrictive file mode after copying the repo with different ownership.
Understand the failure class
Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.
Related errors
- load config: %w
- create config dir: %w
- write config: %w
- read default rule file %q: %w
- read rule file %q for pattern %q: %w
AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02).
Data as JSON: /api/errors/54be143b2ed4336d.
Report an issue: GitHub.