alibaba/open-code-review · error
read project rule %s: %w
Error message
read project rule %s: %w
What it means
Once EvalSymlinks succeeds, the resolved path is read with os.ReadFile. Non-NotExist read failures (EACCES, EISDIR, I/O errors) are wrapped with the rule path. NotExist still returns nil,nil so a repo without a rule file works normally.
Source
Thrown at internal/config/rules/system_rules.go:435
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)
return &pr, nil
}
// Resolve checks each layer in priority order; first match wins. User rules
// replace the system rule by default; rules with merge_system_rule keep the
// matched system rule alongside the user rule.
func (c *composedResolver) Resolve(path string) string {
for _, layer := range []*ProjectRule{c.custom, c.project, c.global} {
if entry := matchProjectRuleEntry(layer, path); entry != nil {
if entry.MergeSystemRule {
return c.mergeWithSystemRule(path, entry.Rule)
}View on GitHub (pinned to 5cf97d0d15)
Solutions
- Fix file permissions: chmod a+r .opencodereview/rule.json or chown to the running user
- Verify the path is a regular file (rm if it is a directory and recreate as a file)
- Validate the JSON is readable: `cat .opencodereview/rule.json`
- Check the wrapped os error for the exact errno
Example fix
// before -rw------- 1 root rule.json // after chmod 644 .opencodereview/rule.json
Defensive patterns
Strategy: validation
Validate before calling
func readableRegularFile(p string) error {
fi, err := os.Stat(p)
if err != nil { return err }
if !fi.Mode().IsRegular() { return fmt.Errorf("%s is not a regular file", p) }
f, err := os.Open(p)
if err != nil { return err }
return f.Close()
} Try / catch
pr, err := loadProjectRule(repoDir)
if err != nil && errors.Is(err, os.ErrPermission) {
fmt.Fprintln(os.Stderr, "fix with: chmod a+r .opencodereview/rule.json")
os.Exit(2)
} else if err != nil { return err } Prevention
- Commit rule.json with 644 permissions
- Never replace rule.json with a directory or symlink to a special file
- Test `cat .opencodereview/rule.json` in CI before running ocr
When it happens
Trigger: os.ReadFile on the symlink-resolved rule.json fails with permission denied, or the path is actually a directory, or a device I/O error occurs.
Common situations: rule.json chmod 600 owned by another user; someone replaced rule.json with a directory of the same name; NFS/EFS mount glitches in CI containers.
Related errors
AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02).
Data as JSON: /api/errors/53636ceed127291f.
Report an issue: GitHub.