{"record":{"id":"4fdf84ff01f68b8f","repo":"crewAIInc/crewAI","slug":"could-not-resolve-hostname-parsed-hostname","errorCode":null,"errorMessage":"Could not resolve hostname: '{parsed.hostname}'","messagePattern":"Could not resolve hostname: '(.+?)'","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/security/safe_path.py","lineNumber":231,"sourceCode":"            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:\n        raise ValueError(f\"Could not resolve hostname: '{parsed.hostname}'\") from exc\n\n    for _family, _, _, _, sockaddr in addrinfos:\n        ip_str = str(sockaddr[0])\n        if _is_private_or_reserved(ip_str):\n            raise ValueError(\n                f\"URL '{url}' resolves to private/reserved IP {ip_str}. \"\n                f\"Access to internal networks is not allowed. \"\n                f\"Set {_UNSAFE_PATHS_ENV}=true to bypass.\"\n            )\n\n    return url\n","sourceCodeStart":213,"sourceCodeEnd":243,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/security/safe_path.py#L213-L243","documentation":"Thrown when socket.getaddrinfo() fails (raises socket.gaierror) while validating a URL's hostname in crewai_tools' SSRF guard. Before any request is made, the library resolves the hostname to check it does not point at private/reserved IPs; if DNS resolution fails, validation aborts with this ValueError. It wraps the underlying gaierror, so the original DNS error is preserved as __cause__.","triggerScenarios":"Passing a URL whose hostname does not exist (typo like 'exmaple.com'), a hostname only resolvable on an internal DNS the machine cannot reach, a machine with no network/DNS configured, or an IPv6 literal/hostname the local resolver refuses.","commonSituations":"CI runners with restricted DNS; air-gapped or proxy-only environments where direct DNS is blocked; typos in configured hostnames; containers with broken resolv.conf; hostnames from LLM-generated tool input.","solutions":["Verify the hostname with a quick check (e.g. `python -c \"import socket; print(socket.getaddrinfo('host', 443))\"` or `nslookup host`) and fix typos.","If the environment requires a proxy for DNS/egress, configure it so direct resolution works, or resolve and validate connectivity from the same container/host the tool runs in.","Check /etc/resolv.conf or container DNS settings if the host is genuinely resolvable elsewhere.","Handle the ValueError in the caller and surface a clear 'unresolvable host' message to the user/agent instead of retrying blindly."],"exampleFix":"// before\nbody, ctype, final = fetch_url_body(\"https://exmaple.com/paper\")\n\n# after\ntry:\n    body, ctype, final = fetch_url_body(\"https://example.com/paper\")\nexcept ValueError as e:\n    if \"Could not resolve hostname\" in str(e):\n        raise RuntimeError(\"Host unresolvable from this environment; check DNS/proxy\") from e\n    raise","handlingStrategy":"retry","validationCode":"import socket\n\ndef resolvable(host: str, port: int = 443) -> bool:\n    try:\n        socket.getaddrinfo(host, port)\n        return True\n    except socket.gaierror:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    validate_url(url)\nexcept ValueError as e:\n    if \"Could not resolve hostname\" in str(e):\n        # transient DNS failures happen; retry with backoff once or twice\n        time.sleep(2)\n        validate_url(url)\n    raise","preventionTips":["Pre-resolve hostnames from the runtime environment (not your laptop) before configuring tool URLs.","In CI/containers, verify DNS works: socket.getaddrinfo on a known host as a startup check.","Distinguish DNS errors from SSRF blocks in your exception handling; they need different fixes."],"tags":["dns","network","ssrf","url-validation","environment"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}