crewAIInc/crewAI · error · ValueError
Path '{format_path_for_display(resolved_path, resolved_base)
Error message
Path '{format_path_for_display(resolved_path, resolved_base)}' is outside the allowed directory. Set {_UNSAFE_PATHS_ENV}=true to bypass this check. What it means
Security guard in crewai_tools.security.safe_path: validate_file_path resolves both the path and the allowed base_dir with os.path.realpath (which follows symlinks and strips ..), then requires the result to sit under the base. Any escape — via ../, absolute paths pointing elsewhere, or symlinks that resolve outside — raises this ValueError. The message names the offending resolved path and the env var escape hatch.
Source
Thrown at lib/crewai-tools/src/crewai_tools/security/safe_path.py:115
path,
)
return os.path.realpath(path)
if base_dir is None:
base_dir = os.getcwd()
resolved_base = os.path.realpath(base_dir)
resolved_path = os.path.realpath(
os.path.join(resolved_base, path) if not os.path.isabs(path) else path
)
# Ensure the resolved path is within the base directory.
# When resolved_base already ends with a separator (e.g. the filesystem
# root "/"), appending os.sep would double it ("//"), so use the base
# as-is in that case.
prefix = resolved_base if resolved_base.endswith(os.sep) else resolved_base + os.sep
if not resolved_path.startswith(prefix) and resolved_path != resolved_base:
raise ValueError(
f"Path '{format_path_for_display(resolved_path, resolved_base)}' is "
f"outside the allowed directory. "
f"Set {_UNSAFE_PATHS_ENV}=true to bypass this check."
)
return resolved_path
def validate_directory_path(path: str, base_dir: str | None = None) -> str:
"""Validate that a directory path is safe to read.
Same as :func:`validate_file_path` but also checks that the path
is an existing directory.
Args:
path: The directory path to validate.
base_dir: Allowed root directory. Defaults to ``os.getcwd()``.
View on GitHub (pinned to 754d7323be)
Solutions
- Pass paths relative to the allowed base directory and let the validator resolve them — avoid absolute paths unless they are genuinely under the base.
- If legitimate files live outside the default base (cwd), pass an explicit base_dir covering them: validate_file_path(path, base_dir='/srv/data').
- Check for symlinks in the path: realpath exposes where they actually point; move the real file inside the base or extend base_dir.
- Only as a last resort in trusted local dev, set the documented env var (_UNSAFE_PATHS_ENV) to true to bypass — never in production.
Example fix
# before
validated = validate_file_path("../../secrets/key.pem") # escapes base
# after
validated = validate_file_path("secrets/key.pem", base_dir="/srv/app")
# or move the file under the allowed base and use its relative path Defensive patterns
Strategy: validation
Validate before calling
import os
def ensure_within_base(path: str, base_dir: str) -> str | None:
base = os.path.realpath(base_dir)
resolved = os.path.realpath(path if os.path.isabs(path) else os.path.join(base, path))
prefix = base if base.endswith(os.sep) else base + os.sep
return resolved if resolved == base or resolved.startswith(prefix) else None Try / catch
try:
validated = validate_file_path(user_path, base_dir=BASE)
except ValueError as e:
if "outside the allowed directory" in str(e):
reject(user_path) # treat as unsafe input, do not retry
raise Prevention
- Never pass raw user/LLM paths to loaders; resolve and contain them against an explicit base first.
- Prefer relative paths plus an explicit base_dir over absolute paths.
- Remember realpath resolves symlinks — audit any symlink inside the base that points outside.
- Keep the unsafe-paths env hatch disabled in production.
When it happens
Trigger: Passing '../../etc/passwd' as a file source; passing an absolute path outside base_dir (default os.getcwd()); a symlink inside the base that points to a file outside it; cases where the base itself is a symlinked path and the caller compares against the un-resolved variant. The env var named by _UNSAFE_PATHS_ENV disables the check entirely when set to true.
Common situations: Agent tools accepting LLM-supplied or user-supplied paths that traverse out of the workspace; deployments where the code directory is a symlink (e.g. /app -> /var/app) and realpath resolves differently than expected; CI running from a different realpath than the configured base.
Related errors
- Invalid skill reference: org and name must be single, non-em
- Path traversal not allowed: {self.path}
- Blocked path traversal attempt: {member.name!r}
- Blocked unsupported tar member: {member.name!r}
- Blocked link target escaping destination: {member.name!r} ->
AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15).
Data as JSON: /api/errors/e30122d08540509d.
Report an issue: GitHub.