{"record":{"id":"4ba5678a8f2462ce","repo":"unslothai/unsloth","slug":"provider-base-url-hostname-could-not-be-resolved","errorCode":null,"errorMessage":"Provider base URL hostname could not be resolved.","messagePattern":"Provider base URL hostname could not be resolved\\.","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/core/inference/providers.py","lineNumber":844,"sourceCode":"        # this reads that rather than starting a second bounded lookup: on a\n        # slow resolver the pair of them would each spend a deadline before the\n        # fallback below spent a third.\n        resolved = _cached_addresses(hostname)\n        if resolved is None:\n            # This path blocked on an unbounded getaddrinfo before the metadata\n            # check existed, and a resolver slower than that check's deadline is\n            # ordinary (the Linux default is 5s per server, twice). Falling back\n            # to the same unbounded call keeps a slow-but-working resolver from\n            # turning into a refusal here, where \"no answer\" fails closed.\n            import socket\n            try:\n                infos = socket.getaddrinfo(\n                    _transport_host(hostname),\n                    port or (443 if scheme == \"https\" else 80),\n                    type = socket.SOCK_STREAM,\n                )\n            except (OSError, UnicodeError) as exc:\n                raise ValueError(\"Provider base URL hostname could not be resolved.\") from exc\n            resolved = tuple(str(info[4][0]) for info in infos)\n        addresses = [ipaddress.ip_address(address.split(\"%\", 1)[0]) for address in resolved]\n    if not addresses or any(not ip.is_global for ip in addresses):\n        raise ValueError(\n            \"Provider base URL points at a private address, which is disabled on this \"\n            f\"server ({_BLOCK_PRIVATE_ENV}=1).\"\n        )\n\n\ndef validate_provider_base_url(base_url: str) -> str:\n    \"\"\"Return a normalized provider base URL, or raise ``ValueError``.\n\n    The backend issues outbound requests to this URL with the caller's decrypted\n    API key attached, so it is caller-controlled server-side egress. Only shapes\n    that can never be a real provider endpoint are refused: a non-http(s) scheme,\n    control characters, a missing host, and cloud metadata services. Plain http,\n    loopback, LAN hosts, odd ports, query strings and basic-auth userinfo all\n    stay valid -- Ollama, llama.cpp, vLLM and custom gateways rely on them. A","sourceCodeStart":826,"sourceCodeEnd":862,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/providers.py#L826-L862","documentation":"Raised in _reject_non_public (active only when UNSLOTH_STUDIO_BLOCK_PRIVATE_PROVIDER_URLS=1) when socket.getaddrinfo for the provider base URL's hostname fails with OSError or UnicodeError. The resolver produced no answer, so the server cannot prove the host is public and fails closed. The surrounding comment notes an earlier fast check was removed, so a slow resolver is tolerated — only a hard resolution failure lands here.","triggerScenarios":"Configuring a custom provider base URL whose hostname has no DNS record (typo, stale internal name) while the private-URL block is enabled; a hostname with invalid unicode; resolver outage on the host.","commonSituations":"Typos like 'api.openai.com.example', internal DNS names that the server's resolver does not know, containers with broken resolv.conf, or a provider domain that was decommissioned.","solutions":["Verify the hostname resolves from the server: dig/nslookup <host>.","Fix typos in the provider base URL configuration.","If the host is genuinely internal, note the private-URL block is opt-in — unset UNSLOTH_STUDIO_BLOCK_PRIVATE_PROVIDER_URLS only if the deployment allows private endpoints.","Repair container/host DNS (resolv.conf, dnsmasq) if resolution is broken server-wide."],"exampleFix":"# before\nvalidate_provider_base_url(\"https://api.openai.com.exmaple/v1\")\n# after\nvalidate_provider_base_url(\"https://api.openai.com/v1\")","handlingStrategy":"validation","validationCode":"import socket\nsocket.getaddrinfo(hostname, 443, type=socket.SOCK_STREAM)  # raises before the API if DNS is broken","typeGuard":"def hostname_resolves(host: str) -> bool:\n    import socket\n    try:\n        socket.getaddrinfo(host, 443, type=socket.SOCK_STREAM)\n        return True\n    except (OSError, UnicodeError):\n        return False","tryCatchPattern":"try:\n    validate_provider_base_url(url)\nexcept ValueError as e:\n    if \"could not be resolved\" in str(e):\n        flag_bad_hostname(url)","preventionTips":["Resolve-check provider hostnames at config time, not request time.","Run resolution checks from the server host — the server's resolver is the one that matters."],"tags":["network","dns","validation","provider-config","ssrf"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}