alibaba/open-code-review · error

resolve file %q: %w

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.

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)

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.