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

  1. 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.
  2. Confirm the file exists in the current working tree: ls <path> relative to RepoDir.
  3. 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

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


AI-assisted analysis of alibaba/open-code-review@5cf97d0d15 (2026-09-02). Data as JSON: /api/errors/70c1d592985387c8. Report an issue: GitHub.