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

  1. Pass paths relative to the allowed base directory and let the validator resolve them — avoid absolute paths unless they are genuinely under the base.
  2. If legitimate files live outside the default base (cwd), pass an explicit base_dir covering them: validate_file_path(path, base_dir='/srv/data').
  3. Check for symlinks in the path: realpath exposes where they actually point; move the real file inside the base or extend base_dir.
  4. 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

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


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/e30122d08540509d. Report an issue: GitHub.