{"record":{"id":"9d4abd2e5862cba1","repo":"crewAIInc/crewAI","slug":"blocked-unsafe-file-path-e","errorCode":null,"errorMessage":"Blocked unsafe file path: {e}","messagePattern":"Blocked unsafe file path: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/tools/rag/rag_tool.py","lineNumber":331,"sourceCode":"                    validate_url(source_ref)\n                except ValueError as e:\n                    raise ValueError(f\"Blocked unsafe URL: {e}\") from e\n                validated_args.append(arg)\n                continue\n\n            # Check if it looks like a file path (not a plain text string).\n            # Check both os.sep (backslash on Windows) and \"/\" so that\n            # forward-slash paths like \"sub/file.txt\" are caught on all platforms.\n            if (\n                os.path.sep in source_ref\n                or \"/\" in source_ref\n                or source_ref.startswith(\".\")\n                or os.path.isabs(source_ref)\n            ):\n                try:\n                    resolved_ref = validate_file_path(source_ref)\n                except ValueError as e:\n                    raise ValueError(f\"Blocked unsafe file path: {e}\") from e\n                # Use the resolved path to prevent symlink TOCTOU\n                if isinstance(arg, dict):\n                    arg = {**arg}\n                    if \"source\" in arg:\n                        arg[\"source\"] = resolved_ref\n                    elif \"content\" in arg:\n                        arg[\"content\"] = resolved_ref\n                else:\n                    arg = resolved_ref\n\n            validated_args.append(arg)\n\n        # Validate keyword path/URL arguments — these are equally user-controlled\n        # and must not bypass the checks applied to positional args.\n        if \"path\" in kwargs and kwargs.get(\"path\") is not None:\n            kwargs[\"path\"] = _check_path(str(kwargs[\"path\"]), \"path\")\n        if \"file_path\" in kwargs and kwargs.get(\"file_path\") is not None:\n            kwargs[\"file_path\"] = _check_path(str(kwargs[\"file_path\"]), \"file_path\")","sourceCodeStart":313,"sourceCodeEnd":349,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/tools/rag/rag_tool.py#L313-L349","documentation":"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>'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass only paths inside the configured allowed root","Check the embedded validate_file_path reason (traversal / absolute / symlink escape) and fix accordingly","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","Symlink legitimate data into the allowed root rather than pointing outside it"],"exampleFix":"# before\nrag_tool.add('../shared/knowledge.pdf')  # ValueError: Blocked unsafe file path\n\n# after\nrag_tool.add('knowledge/knowledge.pdf')  # inside allowed root\n# or for slash-containing TEXT:\nrag_tool.add({'content': 'routes: /api/v1 /api/v2'})\n","handlingStrategy":"validation","validationCode":"import os\nfrom crewai_tools.security.safe_path import validate_file_path\n\ndef addable_path(p: str) -> bool:\n    if not (os.sep in p or \"/\" in p or p.startswith(\".\") or os.path.isabs(p)):\n        return True  # plain text, not treated as path\n    try:\n        validate_file_path(p)\n        return True\n    except ValueError:\n        return False","typeGuard":"def looks_like_path(s: str) -> bool:\n    return os.sep in s or \"/\" in s or s.startswith(\".\") or os.path.isabs(s)","tryCatchPattern":"try:\n    rag_tool.add(item)\nexcept ValueError as e:\n    if \"Blocked unsafe file path\" in str(e):\n        raise PermissionError(f\"path outside allowed root: {item}\") from e\n    raise","preventionTips":["Remember: any string containing '/' is heuristically path-checked — wrap slash-containing prose as {'content': text}","Keep documents inside the allowed root; symlink them in rather than pointing outside","Reject user inputs containing '..' before they reach add()","Use the resolved path returned by the guard (it also defeats symlink TOCTOU)"],"tags":["security","path-traversal","file-validation","rag","guard"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}