{"record":{"id":"7f300162e119b885","repo":"alibaba/open-code-review","slug":"resolve-file-q-w-7f3001","errorCode":null,"errorMessage":"resolve file %q: %w","messagePattern":"resolve file %q: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/tool/filereader.go","lineNumber":110,"sourceCode":"}\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) {\n\t\t\treturn fullPath, nil\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"resolve file %q: %w\", path, err)\n\t}\n\tif !pathutil.WithinBase(repoRoot, resolvedPath) {\n\t\treturn \"\", fmt.Errorf(\"file path %q is outside repository\", path)\n\t}\n\treturn resolvedPath, nil\n}\n\nfunc (fr *FileReader) readFromGitShow(parentCtx context.Context, path string) (string, error) {\n\tctx, cancel := context.WithTimeout(parentCtx, 30*time.Second)\n\tdefer cancel()\n\n\targs := []string{\"-c\", \"core.quotepath=false\", \"show\", \"--end-of-options\", fr.Ref + \":\" + path}\n\tif fr.Runner != nil {\n\t\toutput, err := fr.Runner.Output(ctx, fr.RepoDir, args...)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"git show %s:%s: %w\", fr.Ref, path, err)\n\t\t}\n\t\treturn string(output), nil","sourceCodeStart":92,"sourceCodeEnd":128,"githubUrl":"https://github.com/alibaba/open-code-review/blob/5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f/internal/tool/filereader.go#L92-L128","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Fix filesystem permissions so every component of the path from the repo root is traversable by the current user (chmod +x on directories).","Inspect and repair symlink loops: run `find -L . -type l` or `namei -l <path>` to locate circular symlinks and remove them.","Shorten or restructure overly deep paths if the error is ENAMETOOLONG.","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.","Use errors.Unwrap / errors.Is (os.IsPermission, syscall.ELOOP) on the returned error to identify the exact cause before choosing a fix."],"exampleFix":"// before: unreadable parent dir causes EvalSymlinks EACCES\n$ ls -ld /repo/secrets  # drwx------  root root\n// after\n$ sudo chmod o+x /repo/secrets  # allow traversal for the reader user","handlingStrategy":"try-catch","validationCode":"info, err := os.Stat(filepath.Join(repoDir, path))\nif err == nil {\n    if err := filepath.Walk(repoDir, func(p string, i os.FileInfo, e error) error { return nil }); err != nil { /* traversal issue possible */ }\n}\n// cheaper: resolve symlinks yourself first\nresolved, err := filepath.EvalSymlinks(filepath.Join(repoDir, path))\nif err != nil && !os.IsNotExist(err) { /* same failure will surface from the library */ }","typeGuard":"func isSymlinkResolveErr(err error) bool {\n    var pe *os.PathError\n    if errors.As(err, &pe) {\n        return errors.Is(pe.Err, syscall.ELOOP) || errors.Is(pe.Err, os.ErrPermission) || errors.Is(pe.Err, syscall.ENAMETOOLONG)\n    }\n    return false\n}","tryCatchPattern":"resolved, err := fr.Read(ctx, path)\nif err != nil {\n    if isSymlinkResolveErr(err) {\n        // fall back to git-show mode or skip the file\n        return fallbackRead(ctx, path)\n    }\n    return err\n}","preventionTips":["Check out repositories on local filesystems, not FUSE/network mounts","Audit symlinks in repos before review (find -L . -type l)","Ensure the tool runs as a user with traverse (+x) rights on all parent directories","Prefer ModeRange/ModeCommit (git show) when disk layout is unusual"],"tags":["filesystem","symlink","path-resolution","go"],"backgroundTag":"symlink-resolution-failed","analyzedSha":"5cf97d0d15cbd41b602513c4be3bfec3cee5bf7f","analyzedAt":"2026-09-02T02:08:09.116Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}