{"record":{"id":"70c1d592985387c8","repo":"alibaba/open-code-review","slug":"read-file-q-w-70c1d5","errorCode":null,"errorMessage":"read file %q: %w","messagePattern":"read file %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/tool/filereader.go","lineNumber":89,"sourceCode":"func (fr *FileReader) Read(ctx context.Context, path string) (string, error) {\n\tswitch fr.Mode {\n\tcase ModeWorkspace:\n\t\treturn fr.readFromDisk(path)\n\tcase ModeRange, ModeCommit:\n\t\treturn fr.readFromGitShow(ctx, path)\n\tdefault:\n\t\treturn fr.readFromDisk(path)\n\t}\n}\n\nfunc (fr *FileReader) readFromDisk(path string) (string, error) {\n\tfullPath, err := fr.resolveWorkspacePath(path)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tcontent, err := os.ReadFile(fullPath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"read file %q: %w\", path, err)\n\t}\n\treturn string(content), nil\n}\n\nfunc (fr *FileReader) resolveWorkspacePath(path string) (string, error) {\n\trepoRoot, err := pathutil.CanonicalPath(fr.RepoDir)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"resolve repository path %q: %w\", fr.RepoDir, err)\n\t}\n\n\tfullPath := filepath.Join(repoRoot, path)\n\tif !pathutil.WithinBase(repoRoot, fullPath) {\n\t\treturn \"\", fmt.Errorf(\"file path %q is outside repository\", path)\n\t}\n\n\tresolvedPath, err := filepath.EvalSymlinks(fullPath)\n\tif err != nil {\n\t\tif os.IsNotExist(err) {","sourceCodeStart":71,"sourceCodeEnd":107,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/tool/filereader.go#L71-L107","documentation":"Error from FileReader.readFromDisk when os.ReadFile fails on an already-resolved workspace path. Path resolution (resolveWorkspacePath) has succeeded, so this is a genuine read failure: the file does not exist on the working tree, is a directory, or the process lacks read permission. The original OS error is wrapped via %w for inspection.","triggerScenarios":"Calling FileReader.Read in workspace mode (ModeWorkspace) for a path that passed the within-repo check but then fails os.ReadFile — missing file, EISDIR on directories, EACCES on unreadable files, or a symlink whose target vanished between resolution and read.","commonSituations":"Reviewing a workspace where a file was deleted after being listed; reading generated files that were never built; read-protected config files; passing a directory path where a file path is expected.","solutions":["Check the wrapped OS error (ENOENT vs EISDIR vs EACCES) and act accordingly: recreate the file, pass a file instead of a directory, or fix permissions.","Confirm the file exists in the current working tree: ls <path> relative to RepoDir.","If the file only exists at a git ref, switch the review mode to range/commit so it is read via git show."],"exampleFix":"// before: reading a deleted workspace file\ncontent, err := fr.Read(ctx, \"generated/config.go\")\n// after: read the tracked copy at the review ref instead\nfr.Mode = ModeRange; fr.Ref = \"HEAD~1\"\ncontent, err := fr.Read(ctx, \"cmd/config.go\")","handlingStrategy":"validation","validationCode":"full := filepath.Join(repoRoot, rel)\ninfo, err := os.Stat(full)\nif err != nil { return fmt.Errorf(\"stat %s: %w\", rel, err) }\nif info.IsDir() { return errors.New(\"path is a directory\") }\nif info.Mode().Perm()&0o400 == 0 { return errors.New(\"file not readable\") }","typeGuard":"func readableFile(path string) bool { info, err := os.Stat(path); return err == nil && !info.IsDir() && info.Mode().Perm()&0o400 != 0 }","tryCatchPattern":"content, err := fr.Read(ctx, rel)\nif err != nil {\n    if errors.Is(err, os.ErrNotExist) { return readAtRef(ctx, fr, rel) } // git show fallback\n    if errors.Is(err, fs.ErrPermission) { return nil, fmt.Errorf(\"fix permissions for %s\", rel) }\n    return nil, err\n}","preventionTips":["Stat the file in workspace mode before reading; the file may have been deleted or generated lazily.","Use range/commit mode (git show) for files that only exist at a ref.","Check the process user has read access to all repo files.","Never pass directory paths to Read."],"tags":["file-io","file-not-found","permissions","workspace"],"backgroundTag":"file-not-found","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}