crewAIInc/crewAI · error · ValueError

Blocked unsafe file path: {e}

Error message

Blocked unsafe file path: {e}

What it means

Raised in RAGTool.add()'s per-argument loop when a value 'looks like a file path' (contains a path separator, starts with '.', or is absolute) and validate_file_path rejects it. On success the argument is rewritten to the resolved real path specifically to prevent symlink TOCTOU; on failure the guard's reason is wrapped as 'Blocked unsafe file path: <reason>'.

Source

Thrown at lib/crewai-tools/src/crewai_tools/tools/rag/rag_tool.py:331

                    validate_url(source_ref)
                except ValueError as e:
                    raise ValueError(f"Blocked unsafe URL: {e}") from e
                validated_args.append(arg)
                continue

            # Check if it looks like a file path (not a plain text string).
            # Check both os.sep (backslash on Windows) and "/" so that
            # forward-slash paths like "sub/file.txt" are caught on all platforms.
            if (
                os.path.sep in source_ref
                or "/" in source_ref
                or source_ref.startswith(".")
                or os.path.isabs(source_ref)
            ):
                try:
                    resolved_ref = validate_file_path(source_ref)
                except ValueError as e:
                    raise ValueError(f"Blocked unsafe file path: {e}") from e
                # Use the resolved path to prevent symlink TOCTOU
                if isinstance(arg, dict):
                    arg = {**arg}
                    if "source" in arg:
                        arg["source"] = resolved_ref
                    elif "content" in arg:
                        arg["content"] = resolved_ref
                else:
                    arg = resolved_ref

            validated_args.append(arg)

        # Validate keyword path/URL arguments — these are equally user-controlled
        # and must not bypass the checks applied to positional args.
        if "path" in kwargs and kwargs.get("path") is not None:
            kwargs["path"] = _check_path(str(kwargs["path"]), "path")
        if "file_path" in kwargs and kwargs.get("file_path") is not None:
            kwargs["file_path"] = _check_path(str(kwargs["file_path"]), "file_path")

View on GitHub (pinned to 754d7323be)

Solutions

  1. Pass only paths inside the configured allowed root
  2. Check the embedded validate_file_path reason (traversal / absolute / symlink escape) and fix accordingly
  3. If your text genuinely contains slashes and isn't a path, wrap it as {'content': text} so it isn't heuristically treated as a file
  4. Symlink legitimate data into the allowed root rather than pointing outside it

Example fix

# before
rag_tool.add('../shared/knowledge.pdf')  # ValueError: Blocked unsafe file path

# after
rag_tool.add('knowledge/knowledge.pdf')  # inside allowed root
# or for slash-containing TEXT:
rag_tool.add({'content': 'routes: /api/v1 /api/v2'})
Defensive patterns

Strategy: validation

Validate before calling

import os
from crewai_tools.security.safe_path import validate_file_path

def addable_path(p: str) -> bool:
    if not (os.sep in p or "/" in p or p.startswith(".") or os.path.isabs(p)):
        return True  # plain text, not treated as path
    try:
        validate_file_path(p)
        return True
    except ValueError:
        return False

Type guard

def looks_like_path(s: str) -> bool:
    return os.sep in s or "/" in s or s.startswith(".") or os.path.isabs(s)

Try / catch

try:
    rag_tool.add(item)
except ValueError as e:
    if "Blocked unsafe file path" in str(e):
        raise PermissionError(f"path outside allowed root: {item}") from e
    raise

Prevention

When it happens

Trigger: rag_tool.add('/etc/shadow'), rag_tool.add('../../secrets/.env'), or any path that resolves outside the allowed root — including via symlinks. The path-detection heuristic also fires on any string containing '/', so 'some/relative/file.txt' is treated as a path, not plain text.

Common situations: Agents told to 'read all project files' reaching into .env/keys; symlinked data directories escaping the sandbox; users surprised that text containing slashes is path-validated; containerized runs where allowed roots are mounted differently.

Related errors


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