{"record":{"id":"05b1546b19d1ed79","repo":"hiyouga/LlamaFactory","slug":"could-not-resolve-hostname-parsed-url-hostname","errorCode":null,"errorMessage":"Could not resolve hostname: {parsed_url.hostname}","messagePattern":"Could not resolve hostname: (.+?)","errorType":"http","errorClass":"HTTPException","httpStatus":400,"severity":"error","filePath":"src/llamafactory/api/common.py","lineNumber":92,"sourceCode":"        if parsed_url.scheme not in [\"http\", \"https\"]:\n            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=\"Only HTTP/HTTPS URLs are allowed.\")\n\n        hostname = parsed_url.hostname\n        if not hostname:\n            raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=\"Invalid URL hostname.\")\n\n        ip_info = socket.getaddrinfo(hostname, parsed_url.port)\n        ip_address_str = ip_info[0][4][0]\n        ip = ipaddress.ip_address(ip_address_str)\n\n        if not ip.is_global:\n            raise HTTPException(\n                status_code=status.HTTP_403_FORBIDDEN,\n                detail=\"Access to private or reserved IP addresses is not allowed.\",\n            )\n\n    except socket.gaierror:\n        raise HTTPException(\n            status_code=status.HTTP_400_BAD_REQUEST, detail=f\"Could not resolve hostname: {parsed_url.hostname}\"\n        )\n    except Exception as e:\n        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f\"Invalid URL: {e}\")\n","sourceCodeStart":74,"sourceCodeEnd":97,"githubUrl":"https://github.com/hiyouga/LlamaFactory/blob/f28afaf6355af515454dfb16c97d728307c93897/src/llamafactory/api/common.py#L74-L97","documentation":"Raised as HTTP 400 by check_ssrf_url when socket.getaddrinfo raises socket.gaierror — the hostname in the media URL cannot be resolved via DNS. Resolution happens as part of the SSRF check (to test the IP), so DNS failure surfaces here rather than at fetch time.","triggerScenarios":"Typo'd or expired domains (https://exmaple.com/img.png); internal hostnames not resolvable from the API server's DNS view; DNS outage; URL built from an unconfigured variable.","commonSituations":"Client machine resolves a VPN-only hostname but the API server cannot; stale links in datasets; air-gapped deployments without outbound DNS.","solutions":["Verify the hostname resolves from the API server: `getent hosts <hostname>` / `nslookup <hostname>` on that host.","Fix typos or use an IP/public hostname that both sides can resolve.","For internal-only media, use local paths with ALLOW_LOCAL_FILES or base64 data URLs.","Check the server's /etc/resolv.conf and egress DNS policy if resolution fails globally."],"exampleFix":"# before\nurl: 'https://exmaple.com/img.png'  # typo\n# after\nurl: 'https://example.com/img.png'","handlingStrategy":"validation","validationCode":"import socket\nfrom urllib.parse import urlparse\ndef host_resolvable(u):\n    try:\n        socket.getaddrinfo(urlparse(u).hostname, None)\n        return True\n    except socket.gaierror:\n        return False\n\nassert host_resolvable(media_url)","typeGuard":null,"tryCatchPattern":"catch (e) { if (e.status === 400 && e.detail?.startsWith('Could not resolve hostname')) { verify DNS from the SERVER, not your laptop; fix or switch to data URL; } throw e; }","preventionTips":["Run getaddrinfo checks in the API server's network context, not the client's.","Validate dataset-stored URLs periodically for rot.","Prefer IPs or public names in air-gapped setups where DNS is unreliable."],"tags":["security","ssrf","dns","network","http-400"],"backgroundTag":null,"analyzedSha":"f28afaf6355af515454dfb16c97d728307c93897","analyzedAt":"2026-08-14T21:57:28.298Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}