alibaba/open-code-review · error

resolve file

Error message

resolve file %q: %w

What it means

This error wraps a failure from filepath.EvalSymlinks while resolving a workspace file path in FileReader.resolveWorkspacePath (internal/tool/filereader.go:110). The library calls EvalSymlinks to canonicalize the path so it can verify the real target stays inside the repository. A missing file is tolerated (returned as-is), but any other resolution failure — permission problems, symlink loops, or I/O errors on a path component — is wrapped as "resolve file %q: %w". It surfaces from FileReader.Read or FileReader.ReadLines in workspace mode.

Solutions

  1. Fix filesystem permissions so every component of the path from the repo root is traversable by the current user (chmod +x on directories).
  2. Inspect and repair symlink loops: run `find -L . -type l` or `namei -l <path>` to locate circular symlinks and remove them.
  3. Shorten or restructure overly deep paths if the error is ENAMETOOLONG.
  4. If the path is on a network/FUSE mount, retry after the mount is healthy, or read the file via git-show mode instead of workspace mode.
  5. Use errors.Unwrap / errors.Is (os.IsPermission, syscall.ELOOP) on the returned error to identify the exact cause before choosing a fix.

Example fix

// before: unreadable parent dir causes EvalSymlinks EACCES
$ ls -ld /repo/secrets  # drwx------  root root
// after
$ sudo chmod o+x /repo/secrets  # allow traversal for the reader user
Defensive patterns

Strategy: try-catch

Validate before calling

info, err := os.Stat(filepath.Join(repoDir, path))
if err == nil {
    if err := filepath.Walk(repoDir, func(p string, i os.FileInfo, e error) error { return nil }); err != nil { /* traversal issue possible */ }
}
// cheaper: resolve symlinks yourself first
resolved, err := filepath.EvalSymlinks(filepath.Join(repoDir, path))
if err != nil && !os.IsNotExist(err) { /* same failure will surface from the library */ }

Type guard

func isSymlinkResolveErr(err error) bool {
    var pe *os.PathError
    if errors.As(err, &pe) {
        return errors.Is(pe.Err, syscall.ELOOP) || errors.Is(pe.Err, os.ErrPermission) || errors.Is(pe.Err, syscall.ENAMETOOLONG)
    }
    return false
}

Try / catch

resolved, err := fr.Read(ctx, path)
if err != nil {
    if isSymlinkResolveErr(err) {
        // fall back to git-show mode or skip the file
        return fallbackRead(ctx, path)
    }
    return err
}

Prevention

When it happens

Trigger: Calling FileReader.Read/ReadLines in ModeWorkspace when EvalSymlinks fails with a non-ENOENT error: e.g. a directory component of the path is not readable (EACCES during resolution), the path contains a symlink loop (ELOOP), the path is too long (ENAMETOOLONG), or an I/O error occurs while walking a component. Purely nonexistent files do NOT trigger this — they return the unresolved full path instead.

Common situations: Repository checked out under a directory with restrictive permissions so path traversal fails mid-resolution; symlink chains pointing back at each other (common after bad manual linking or FUSE/network mounts); path components exceeding filesystem name limits; NFS or container volume mounts returning transient I/O errors.

Related errors


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

Appendix: source

Thrown at internal/tool/filereader.go:110

}

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) {
			return fullPath, nil
		}
		return "", fmt.Errorf("resolve file %q: %w", path, err)
	}
	if !pathutil.WithinBase(repoRoot, resolvedPath) {
		return "", fmt.Errorf("file path %q is outside repository", path)
	}
	return resolvedPath, nil
}

func (fr *FileReader) readFromGitShow(parentCtx context.Context, path string) (string, error) {
	ctx, cancel := context.WithTimeout(parentCtx, 30*time.Second)
	defer cancel()

	args := []string{"-c", "core.quotepath=false", "show", "--end-of-options", fr.Ref + ":" + path}
	if fr.Runner != nil {
		output, err := fr.Runner.Output(ctx, fr.RepoDir, args...)
		if err != nil {
			return "", fmt.Errorf("git show %s:%s: %w", fr.Ref, path, err)
		}
		return string(output), nil

View on GitHub (pinned to 5cf97d0d15)