{"record":{"id":"54be143b2ed4336d","repo":"alibaba/open-code-review","slug":"read-rule-file-s-w","errorCode":null,"errorMessage":"read rule file %s: %w","messagePattern":"read rule file (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/rules/system_rules.go","lineNumber":396,"sourceCode":"\tdata, err := os.ReadFile(path)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"read global rule %s: %w\", path, err)\n\t}\n\tvar pr ProjectRule\n\tif err := json.Unmarshal(data, &pr); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal global rule: %w\", err)\n\t}\n\tresolveRuleEntries(pr.Rules, filepath.Dir(path), \"\")\n\treturn &pr, nil\n}\n\nfunc loadRuleFile(path string) (*ProjectRule, error) {\n\tdata, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"read rule file %s: %w\", path, err)\n\t}\n\tvar pr ProjectRule\n\tif err := json.Unmarshal(data, &pr); err != nil {\n\t\treturn nil, fmt.Errorf(\"unmarshal rule file %s: %w\", path, err)\n\t}\n\tresolveRuleEntries(pr.Rules, filepath.Dir(path), \"\")\n\treturn &pr, nil\n}\n\n// loadProjectRule reads <repoDir>/.opencodereview/rule.json. Since #287 anchored\n// RepoDir at the git top-level, `ocr review` from a monorepo subdirectory loads\n// the repo-root rule file — which is consistent, since rule entries match against\n// root-relative diff paths. A subproject-local rule.json under the subdirectory is\n// intentionally not consulted; put shared rules at the repo root, or pass --rule.\nfunc loadProjectRule(repoDir string) (*ProjectRule, error) {\n\tconfineRoot, err := pathutil.CanonicalPath(repoDir)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"resolve repo dir %s: %w\", repoDir, err)","sourceCodeStart":378,"sourceCodeEnd":414,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/config/rules/system_rules.go#L378-L414","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: file missing\n// err: read rule file /repo/.opencodereview/rule.json: open ...: no such file or directory\n// after\n$ mkdir -p .opencodereview && printf '{\"rules\": []}' > .opencodereview/rule.json","handlingStrategy":"validation","validationCode":"p := filepath.Join(repoDir, \".opencodereview\", \"rule.json\")\nif _, err := os.Stat(p); errors.Is(err, os.ErrNotExist) {\n\treturn fmt.Errorf(\"project rule file missing: %s (create it or unset the project-rule path)\", p)\n}","typeGuard":null,"tryCatchPattern":"pr, err := loadRuleFile(path)\nif err != nil {\n\tif errors.Is(err, os.ErrNotExist) { // errors.Is works through the %w wrap\n\t\tlog.Printf(\"no project rule file at %s; using defaults\", path)\n\t\treturn nil, nil\n\t}\n\treturn err\n}","preventionTips":["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)"],"tags":["filesystem","config","missing-file"],"backgroundTag":"file-read-failed","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}