googleapis/mcp-toolbox · error
path %q cannot be resolved: %w
Error message
path %q cannot be resolved: %w
What it means
ResolveWithinDir verifies that a user-supplied relative path stays inside an allowed directory after symlink resolution. This error is returned when the destination path cannot be resolved via filepath.EvalSymlinks/ResolveSymlinks (e.g. broken symlink, permission failure, path not existing in a way EvalSymlinks rejects). The underlying OS error is wrapped with %w so the root cause (ENOENT, EACCES, ELOOP) is preserved.
Source
Thrown at internal/tools/cloudstorage/cloudstoragecommon/paths.go:152
cleanDest := filepath.Clean(filepath.Join(cleanDir, rel))
out, err := escapes(cleanDir, cleanDest)
if err != nil {
return "", fmt.Errorf("path %q cannot be resolved within %q: %w", rel, cleanDir, err)
}
if out {
return "", fmt.Errorf("path %q escapes configured directory %q", rel, cleanDir)
}
// Repeat the check against the real targets. A symlink under cleanDir can
// point anywhere, so the name-level check above proves nothing on its own.
resolvedDir, err := ResolveSymlinks(cleanDir)
if err != nil {
return "", fmt.Errorf("directory %q cannot be resolved: %w", cleanDir, err)
}
resolvedDest, err := ResolveSymlinks(cleanDest)
if err != nil {
return "", fmt.Errorf("path %q cannot be resolved: %w", rel, err)
}
out, err = escapes(resolvedDir, resolvedDest)
if err != nil {
return "", fmt.Errorf("path %q cannot be resolved within %q: %w", rel, cleanDir, err)
}
if out {
return "", fmt.Errorf("path %q resolves through a symbolic link to a target outside configured directory %q", rel, cleanDir)
}
return cleanDest, nil
}
View on GitHub (pinned to 8cc6e09de2)
Solutions
- Inspect the wrapped %w cause with errors.Unwrap/errors.Is to identify the OS error (ENOENT/EACCES/ELOOP)
- Remove or repair the dangling symlink at the destination path
- Ensure the process has read+execute permission on every directory component of the path
- Re-check the path with `ls -la` / `readlink -f` on the host before retrying
Example fix
// before
resolvedDest, err := ResolveSymlinks(cleanDest) // fails: dangling symlink
// after
if _, err := os.Lstat(cleanDest); err == nil {
if fi, err := os.Stat(cleanDest); err != nil && os.IsNotExist(err) {
os.Remove(cleanDest) // remove dangling symlink before resolving
}
}
resolvedDest, err := ResolveSymlinks(cleanDest) Defensive patterns
Strategy: validation
Validate before calling
func pathResolvable(p string) error {
if _, err := filepath.EvalSymlinks(p); err != nil {
return fmt.Errorf("path %q not resolvable: %w", p, err)
}
return nil
}
if err := pathResolvable(dest); err != nil { return err } Type guard
func isSymlink(p string) bool {
fi, err := os.Lstat(p)
return err == nil && fi.Mode()&os.ModeSymlink != 0
} Try / catch
resolved, err := ResolveWithinDir(baseDir, userPath)
if err != nil {
var perr *os.PathError
if errors.As(err, &perr) { log.Printf("OS cause: %v", perr.Err) }
return fmt.Errorf("invalid destination: %w", err)
} Prevention
- Lstat the path before use to detect dangling symlinks
- Ensure service account has traverse (x) permission on all parent dirs
- Clean up stale symlinks in shared volumes after restarts
- Log errors.Unwrap chains to capture the OS-level cause
When it happens
Trigger: Calling ResolveWithinDir (directly or via a cloud-storage tool's Invoke) with a 'dest' path containing a dangling symlink or an unreadable path component so ResolveSymlinks(cleanDest) returns an error.
Common situations: User provides a destination object path that includes a symlink pointing to a deleted file; running as a service account lacking execute permission on a directory component; container volumes with stale symlinks after a restart.
Related errors
- local path %q cannot be resolved for source %q: %w
- path %q resolves through a symbolic link to a target outside
- local path %q is not under any allowed local roots for sourc
- local path %q resolves through a symbolic link to a target o
- unable to read config file at %q: %w
AI-assisted analysis of googleapis/mcp-toolbox@8cc6e09de2 (2026-09-05).
Data as JSON: /api/errors/aea61c48e85f1c04.
Report an issue: GitHub.