alibaba/open-code-review · error
read file %q: %w
Error message
read file %q: %w
What it means
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.
Source
Thrown at internal/tool/filereader.go:89
func (fr *FileReader) Read(ctx context.Context, path string) (string, error) {
switch fr.Mode {
case ModeWorkspace:
return fr.readFromDisk(path)
case ModeRange, ModeCommit:
return fr.readFromGitShow(ctx, path)
default:
return fr.readFromDisk(path)
}
}
func (fr *FileReader) readFromDisk(path string) (string, error) {
fullPath, err := fr.resolveWorkspacePath(path)
if err != nil {
return "", err
}
content, err := os.ReadFile(fullPath)
if err != nil {
return "", fmt.Errorf("read file %q: %w", path, err)
}
return string(content), nil
}
func (fr *FileReader) resolveWorkspacePath(path string) (string, error) {
repoRoot, err := pathutil.CanonicalPath(fr.RepoDir)
if err != nil {
return "", fmt.Errorf("resolve repository path %q: %w", fr.RepoDir, err)
}
fullPath := filepath.Join(repoRoot, path)
if !pathutil.WithinBase(repoRoot, fullPath) {
return "", fmt.Errorf("file path %q is outside repository", path)
}
resolvedPath, err := filepath.EvalSymlinks(fullPath)
if err != nil {
if os.IsNotExist(err) {View on GitHub (pinned to 5cf97d0d15)
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.
Example fix
// before: reading a deleted workspace file content, err := fr.Read(ctx, "generated/config.go") // after: read the tracked copy at the review ref instead fr.Mode = ModeRange; fr.Ref = "HEAD~1" content, err := fr.Read(ctx, "cmd/config.go")
Defensive patterns
Strategy: validation
Validate before calling
full := filepath.Join(repoRoot, rel)
info, err := os.Stat(full)
if err != nil { return fmt.Errorf("stat %s: %w", rel, err) }
if info.IsDir() { return errors.New("path is a directory") }
if info.Mode().Perm()&0o400 == 0 { return errors.New("file not readable") } Type guard
func readableFile(path string) bool { info, err := os.Stat(path); return err == nil && !info.IsDir() && info.Mode().Perm()&0o400 != 0 } Try / catch
content, err := fr.Read(ctx, rel)
if err != nil {
if errors.Is(err, os.ErrNotExist) { return readAtRef(ctx, fr, rel) } // git show fallback
if errors.Is(err, fs.ErrPermission) { return nil, fmt.Errorf("fix permissions for %s", rel) }
return nil, err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "File not found" and ENOENT errors: why libraries can't find a file that should exist — this error's family across 50 libraries.
Related errors
- resolve repository path %q: %w
- read app config %s: %w
- load config: %w
- create config dir: %w
- write config: %w
AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02).
Data as JSON: /api/errors/70c1d592985387c8.
Report an issue: GitHub.