{"record":{"id":"03e5d465d9615b7a","repo":"alibaba/open-code-review","slug":"resolve-repo-dir-s-w","errorCode":null,"errorMessage":"resolve repo dir %s: %w","messagePattern":"resolve repo dir (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/config/rules/system_rules.go","lineNumber":414,"sourceCode":"\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)\n\t}\n\n\tpath := filepath.Join(repoDir, \".opencodereview\", \"rule.json\")\n\tresolved, err := filepath.EvalSymlinks(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(\"resolve project rule %s: %w\", path, err)\n\t}\n\tif !pathutil.WithinBase(confineRoot, resolved) {\n\t\tfmt.Fprintf(os.Stderr, \"[ocr] WARNING: project rule file escapes repo dir: %s\\n\", path)\n\t\treturn nil, nil\n\t}\n\n\tdata, err := os.ReadFile(resolved)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/config/rules/system_rules.go#L396-L432","documentation":"loadProjectRule first canonicalizes the repository directory via pathutil.CanonicalPath before locating .opencodereview/rule.json. If that canonicalization fails (e.g. the path cannot be made absolute/clean because the directory is missing or the path is malformed), the error is wrapped with the repo dir for context. It is thrown by NewResolver during resolver construction, so review runs abort early.","triggerScenarios":"NewResolver is invoked with a --repo or repoDir value that CanonicalPath cannot resolve: an empty path, a nonexistent directory, or a path that fails the underlying transformation.","commonSituations":"Running `ocr review` outside a git repo so the detected repo dir is empty; passing a typo'd --repo flag; a CI checkout that has not happened yet so the target dir does not exist.","solutions":["Run the tool from inside an existing git repository or pass --repo with a valid absolute directory","Check that the repo dir actually exists before invoking ocr (e.g. `ls <repoDir>`)","Inspect the wrapped inner error (%w) from pathutil.CanonicalPath to see the root cause"],"exampleFix":"// before\nocr review --repo ./rpovider-service\n// after\nocr review --repo ./provider-service","handlingStrategy":"validation","validationCode":"import \"path/filepath\"\nfunc ensureRepoDir(dir string) error {\n    if dir == \"\" { return fmt.Errorf(\"repo dir is empty\") }\n    abs, err := filepath.Abs(dir)\n    if err != nil { return err }\n    info, err := os.Stat(abs)\n    if err != nil { return fmt.Errorf(\"repo dir %s: %w\", abs, err) }\n    if !info.IsDir() { return fmt.Errorf(\"%s is not a directory\", abs) }\n    return nil\n}","typeGuard":null,"tryCatchPattern":"rule, err := rules.NewResolver(repoDir)\nif err != nil {\n    var pathErr *os.PathError\n    if errors.As(err, &pathErr) {\n        fmt.Fprintf(os.Stderr, \"bad repo dir %s: %v\\n\", pathErr.Path, pathErr.Err)\n        os.Exit(2)\n    }\n    return err\n}","preventionTips":["Always run ocr from inside a checked-out git repository","Validate the --repo path exists and is a directory before invoking","Prefer absolute paths in scripts/CI to avoid cwd confusion"],"tags":["go","config","filesystem","path-resolution"],"backgroundTag":"invalid-path","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}