alibaba/open-code-review · error

resolve project rule %s: %w

Error message

resolve project rule %s: %w

What it means

After building the rule.json path, loadProjectRule calls filepath.EvalSymlinks to resolve symlinks. Any EvalSymlinks failure other than os.IsNotExist (permission problems, symlink loops, I/O errors) is wrapped as this error. A missing rule.json is intentionally not an error — nil is returned.

Source

Thrown at internal/config/rules/system_rules.go:423

// 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)
	}

	path := filepath.Join(repoDir, ".opencodereview", "rule.json")
	resolved, err := filepath.EvalSymlinks(path)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("resolve project rule %s: %w", path, err)
	}
	if !pathutil.WithinBase(confineRoot, resolved) {
		fmt.Fprintf(os.Stderr, "[ocr] WARNING: project rule file escapes repo dir: %s\n", path)
		return nil, nil
	}

	data, err := os.ReadFile(resolved)
	if err != nil {
		if os.IsNotExist(err) {
			return nil, nil
		}
		return nil, fmt.Errorf("read project rule %s: %w", path, err)
	}
	var pr ProjectRule
	if err := json.Unmarshal(data, &pr); err != nil {
		return nil, fmt.Errorf("unmarshal project rule: %w", err)
	}
	resolveRuleEntries(pr.Rules, repoDir, confineRoot)

View on GitHub (pinned to 5cf97d0d15)

Solutions

  1. Check permissions on .opencodereview/ and its parents (chmod/chown so the ocr user can traverse)
  2. Remove or repair the broken/dangling symlink at .opencodereview/rule.json
  3. Read the wrapped EvalSymlinks error to identify the failing path component
  4. If no project rule is needed, you can delete the .opencodereview/rule.json entry entirely

Example fix

// before
ls -l .opencodereview/rule.json   # dangling symlink
// after
rm .opencodereview/rule.json   # or ln -sf /real/path/rule.json .opencodereview/rule.json
Defensive patterns

Strategy: try-catch

Validate before calling

func checkRuleSymlink(repoDir string) error {
    p := filepath.Join(repoDir, ".opencodereview", "rule.json")
    fi, err := os.Lstat(p)
    if os.IsNotExist(err) { return nil }
    if err != nil { return err }
    if fi.Mode()&os.ModeSymlink != 0 {
        if _, err := filepath.EvalSymlinks(p); err != nil {
            return fmt.Errorf("broken symlink %s: %w", p, err)
        }
    }
    return nil
}

Try / catch

pr, err := loadProjectRule(repoDir)
if err != nil {
    if errors.Is(err, os.ErrPermission) {
        fmt.Fprintln(os.Stderr, "cannot access .opencodereview/rule.json: check permissions")
        os.Exit(2)
    }
    return err
}

Prevention

When it happens

Trigger: filepath.EvalSymlinks on <repoDir>/.opencodereview/rule.json fails with a non-NotExist error: unreadable parent directory, dangling symlink with inaccessible components, ELOOP, or EACCES on traversal.

Common situations: A .opencodereview directory created by root with restrictive permissions while ocr runs as another user; a symlink chain pointing to a mounted volume that is offline; a broken symlink whose target path components are not listable.

Related errors


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