{"record":{"id":"2c3302633c3977ba","repo":"unslothai/unsloth","slug":"provider-base-url-is-malformed","errorCode":null,"errorMessage":"Provider base URL is malformed.","messagePattern":"Provider base URL is malformed\\.","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"error","filePath":"studio/backend/core/inference/providers.py","lineNumber":881,"sourceCode":"    caller-supplied hostname is resolved far enough to apply the metadata block\n    to DNS aliases of it; rejecting other private addresses stays opt-in.\n\n    Normalization is strip + trailing-slash removal only (what the client did\n    before), so validating an already-validated URL returns it unchanged.\n    \"\"\"\n    if not isinstance(base_url, str) or not base_url.strip():\n        raise ValueError(\"Provider base URL is required.\")\n\n    raw = base_url.strip()\n    if any(char.isspace() or ord(char) < 32 or ord(char) == 127 for char in raw) or \"\\\\\" in raw:\n        raise ValueError(\"Provider base URL contains invalid characters.\")\n\n    try:\n        parts = urlsplit(raw)\n        port = parts.port\n        hostname = parts.hostname\n    except ValueError as exc:\n        raise ValueError(\"Provider base URL is malformed.\") from exc\n\n    scheme = parts.scheme.lower()\n    if scheme not in (\"http\", \"https\"):\n        raise ValueError(\"Provider base URL must use http or https.\")\n    # Userinfo stays allowed for gateways behind basic auth; the checks below read\n    # the parsed hostname, so http://api.openai.com@169.254.169.254/ is caught.\n    if not hostname:\n        raise ValueError(\"Provider base URL must contain a hostname.\")\n\n    hostname = hostname.rstrip(\".\")\n    if _metadata_host(hostname) or _resolves_to_metadata(hostname, port, scheme):\n        raise ValueError(\"Cloud metadata endpoints cannot be used as a provider base URL.\")\n\n    if os.environ.get(_BLOCK_PRIVATE_ENV) == \"1\":\n        _reject_non_public(hostname, port, scheme)\n\n    return raw.rstrip(\"/\")\n","sourceCodeStart":863,"sourceCodeEnd":899,"githubUrl":"https://github.com/unslothai/unsloth/blob/203007d19051dcd2ae33876786d117c99f6b0368/studio/backend/core/inference/providers.py#L863-L899","documentation":"urlsplit (or its .port/.hostname access) raised ValueError while parsing the base URL — typically an unparseable port like 'https://host:abc/' or other structurally broken URL syntax. The original exception is chained, preserving the parse reason.","triggerScenarios":"Base URLs like 'https://host:notaport/v1' (non-numeric port), 'https://host:99999/' (out-of-range port), or other urlsplit-invalid syntax.","commonSituations":"Hand-edited config with a typo in the port; template interpolation producing ':$PORT' literally when the variable is empty; URL fragments missing slashes like 'host:8080/path' parsed as a scheme.","solutions":["Check the port segment — it must be a decimal number in 0-65535.","Include the scheme and slashes: 'https://host:8080/v1', not 'host:8080/v1'.","Validate with a URL parser client-side before submitting."],"exampleFix":"# before\nvalidate_provider_base_url(\"https://api.example.com:v1\")\n# after\nvalidate_provider_base_url(\"https://api.example.com/v1\")","handlingStrategy":"validation","validationCode":"from urllib.parse import urlsplit\ntry:\n    urlsplit(raw)._hostinfo  # raises ValueError on bad port before the API sees it\nexcept ValueError:\n    reject_early(raw)","typeGuard":"def parses_as_url(raw: str) -> bool:\n    from urllib.parse import urlsplit\n    try:\n        urlsplit(raw).port\n        return True\n    except ValueError:\n        return False","tryCatchPattern":null,"preventionTips":["Client-side parse-validate URLs before submitting.","Beware template interpolation emitting literal ':$PORT' when the variable is empty."],"tags":["validation","url-parsing","provider-config"],"backgroundTag":null,"analyzedSha":"203007d19051dcd2ae33876786d117c99f6b0368","analyzedAt":"2026-08-15T02:48:39.846Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}