{"record":{"id":"87bb0bb8d5a09378","repo":"crewAIInc/crewAI","slug":"file-urls-are-not-allowed-url-use-a-file","errorCode":null,"errorMessage":"file:// URLs are not allowed: '{url}'. Use a file path instead, or set {_UNSAFE_PATHS_ENV}=true to bypass.","messagePattern":"file:// URLs are not allowed: '(.+?)'\\. Use a file path instead, or set (.+?)=true to bypass\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/security/safe_path.py","lineNumber":212,"sourceCode":"        The validated URL string.\n\n    Raises:\n        ValueError: If the URL uses a blocked scheme or resolves to a\n            private/reserved IP address.\n    \"\"\"\n    if _is_escape_hatch_enabled():\n        logger.warning(\n            \"%s is enabled — skipping URL validation for: %s\",\n            _UNSAFE_PATHS_ENV,\n            url,\n        )\n        return url\n\n    parsed = urlparse(url)\n\n    # Block file:// scheme\n    if parsed.scheme == \"file\":\n        raise ValueError(\n            f\"file:// URLs are not allowed: '{url}'. \"\n            f\"Use a file path instead, or set {_UNSAFE_PATHS_ENV}=true to bypass.\"\n        )\n\n    # Only allow http and https\n    if parsed.scheme not in (\"http\", \"https\"):\n        raise ValueError(\n            f\"URL scheme '{parsed.scheme}' is not allowed. Only http and https are supported.\"\n        )\n\n    if not parsed.hostname:\n        raise ValueError(f\"URL has no hostname: '{url}'\")\n\n    try:\n        addrinfos = socket.getaddrinfo(\n            parsed.hostname, parsed.port or (443 if parsed.scheme == \"https\" else 80)\n        )\n    except socket.gaierror as exc:","sourceCodeStart":194,"sourceCodeEnd":230,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/security/safe_path.py#L194-L230","documentation":"URL validation guard in safe_path blocking the file:// scheme. Because file:// URLs bypass the path-containment checks (they encode arbitrary absolute paths), the validator rejects them outright and only permits http/https. The error suggests using a plain file path (which goes through validate_file_path containment) or enabling the unsafe-paths env hatch.","triggerScenarios":"Calling a URL-validating API with file:///etc/passwd, file:///home/user/doc.html, or a source string that urlparse classifies with scheme 'file'. Happens when the same input pipe accepts both URLs and paths and a user/LLM supplies a file URL; also when local HTML is referenced as file:// during local testing.","commonSituations":"Local testing where developers convert local paths to file:// URLs for uniformity; LLM tool outputs producing file:// links scraped from documentation; hardening tests (pentest) probing for local file inclusion via file:// — this guard is exactly the mitigation.","solutions":["Convert file:// URLs to plain filesystem paths and pass them through the file-path API (which enforces base_dir containment): urllib.parse.urlparse(u).path.","If you truly need file URLs in a trusted local context, set the documented _UNSAFE_PATHS_ENV=true — never in production or with untrusted input.","Sanitize user/LLM-supplied sources before they reach the validator: rewrite file:// entries to paths or reject them with your own message.","Do not attempt scheme tricks (FILE://, file:\\\\\\) — urlparse lowercases the scheme, they are also blocked."],"exampleFix":"# before\nvalidate_url(\"file:///srv/data/doc.html\")  # ValueError\n\n# after\nfrom urllib.parse import urlparse\nu = \"file:///srv/data/doc.html\"\nassert urlparse(u).scheme == \"file\"\nvalidated_path = validate_file_path(urlparse(u).path, base_dir=\"/srv/data\")","handlingStrategy":"validation","validationCode":"from urllib.parse import urlparse\n\ndef is_web_url(url: str) -> bool:\n    return urlparse(url).scheme in (\"http\", \"https\") and bool(urlparse(url).hostname)","typeGuard":null,"tryCatchPattern":"try:\n    validated = validate_url(candidate)\nexcept ValueError as e:\n    if \"file:// URLs are not allowed\" in str(e):\n        candidate = convert_file_url_to_path(candidate)  # urlparse(u).path + path validation\n        validated = validate_file_path(candidate, base_dir=BASE)\n    else:\n        raise","preventionTips":["Normalize file:// inputs to plain paths at your input boundary.","Sanitize LLM/user-supplied source lists before they reach URL validators.","Treat this error as a security control firing — fix the caller, not the guard."],"tags":["security","url-validation","ssrf","filesystem"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}