{"record":{"id":"c87fec8808b506b8","repo":"crewAIInc/crewAI","slug":"blocked-unsafe-label-e","errorCode":null,"errorMessage":"Blocked unsafe {label}: {e}","messagePattern":"Blocked unsafe (.+?): (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/tools/rag/rag_tool.py","lineNumber":288,"sourceCode":"\n            # Keyword argument (documented API)\n            rag_tool.add(path=\"path/to/document.pdf\", data_type=\"file\")\n            rag_tool.add(file_path=\"path/to/document.pdf\", data_type=\"pdf_file\")\n\n            # Auto-detect type from extension\n            rag_tool.add(\"path/to/document.pdf\")  # auto-detects PDF\n        \"\"\"\n        # Validate file paths and URLs before adding to prevent\n        # unauthorized file reads and SSRF.\n        from urllib.parse import urlparse\n\n        from crewai_tools.security.safe_path import validate_file_path, validate_url\n\n        def _check_url(value: str, label: str) -> None:\n            try:\n                validate_url(value)\n            except ValueError as e:\n                raise ValueError(f\"Blocked unsafe {label}: {e}\") from e\n\n        def _check_path(value: str, label: str) -> str:\n            try:\n                return validate_file_path(value)\n            except ValueError as e:\n                raise ValueError(f\"Blocked unsafe {label}: {e}\") from e\n\n        validated_args: list[ContentItem] = []\n        for arg in args:\n            source_ref = (\n                str(arg.get(\"source\", arg.get(\"content\", \"\")))\n                if isinstance(arg, dict)\n                else str(arg)\n            )\n\n            # Check if it's a URL — only catch urlparse-specific errors here;\n            # validate_url's ValueError must propagate so it is never silently bypassed.\n            try:","sourceCodeStart":270,"sourceCodeEnd":306,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/tools/rag/rag_tool.py#L270-L306","documentation":"Raised by RAGTool.add() when a URL argument fails CrewAI's SSRF guard (validate_url). Every URL-scheme argument is checked before being handed to the storage layer; if the URL targets a blocked host (loopback/private/reserved IPs, non-http(s) schemes, credentials in URL, etc.) the guard's reason is wrapped as 'Blocked unsafe URL: <reason>'. The {label} variant appears in the shared _check_url/_check_path helpers used for typed content items.","triggerScenarios":"Calling rag_tool.add('http://169.254.169.254/latest/meta-data') or any URL resolving to loopback/private/reserved ranges, non-http(s) schemes, or otherwise disallowed targets — enforced by crewai_tools.security.safe_path.validate_url before ingestion.","commonSituations":"Agents instructed to fetch internal endpoints (localhost dev servers, 10.x/192.168.x hosts, cloud metadata IPs); legitimate intranet ingestion being blocked by the SSRF guard; passing URLs with embedded userinfo.","solutions":["If the target is genuinely safe, ingest its content directly: fetch it yourself from the allowed network context and pass the text/blob via a content item instead of a URL","Check the wrapped reason (e) — it states exactly why the URL was rejected","For local testing, expose the content via a public tunnel or write it to a file and add the file path instead","Never disable the guard in shared/production code — it exists to stop agent-driven SSRF"],"exampleFix":"# before\nrag_tool.add('http://localhost:8000/docs/index.html')  # ValueError: Blocked unsafe URL\n\n# after\nimport requests\ntext = requests.get('http://localhost:8000/docs/index.html', timeout=10).text\nrag_tool.add({'content': text})  # plain content bypasses URL fetching entirely\n","handlingStrategy":"validation","validationCode":"from crewai_tools.security.safe_path import validate_url\n\ndef url_safe(u: str) -> bool:\n    try:\n        validate_url(u)\n        return True\n    except ValueError:\n        return False\n\nassert url_safe(target_url), f\"URL rejected by SSRF guard: {target_url}\"","typeGuard":null,"tryCatchPattern":"try:\n    rag_tool.add(url)\nexcept ValueError as e:\n    if str(e).startswith(\"Blocked unsafe\"):\n        # fetch content from an allowed context and add as text instead\n        text = fetch_via_proxy(url)\n        rag_tool.add({\"content\": text})\n    else:\n        raise","preventionTips":["Pre-validate URLs with crewai_tools.security.safe_path.validate_url before calling add()","Never point RAG ingestion at loopback/private/metadata IPs","Pass local file content as {'content': text} rather than internal URLs","Treat 'Blocked unsafe' as a security decision, not a bug to work around"],"tags":["security","ssrf","url-validation","rag","guard"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}