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

  1. Check the path from the error message: ls -la <repoDir>/.opencodereview/ and create the rule.json if absent
  2. Verify you're running from the repo the config belongs to (the path is anchored to the repo directory since #287)
  3. 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

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


AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02). Data as JSON: /api/errors/54be143b2ed4336d. Report an issue: GitHub.