unslothai/unsloth · error · ValueError

Provider base URL is malformed.

Error message

Provider base URL is malformed.

What it means

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.

Source

Thrown at studio/backend/core/inference/providers.py:881

    caller-supplied hostname is resolved far enough to apply the metadata block
    to DNS aliases of it; rejecting other private addresses stays opt-in.

    Normalization is strip + trailing-slash removal only (what the client did
    before), so validating an already-validated URL returns it unchanged.
    """
    if not isinstance(base_url, str) or not base_url.strip():
        raise ValueError("Provider base URL is required.")

    raw = base_url.strip()
    if any(char.isspace() or ord(char) < 32 or ord(char) == 127 for char in raw) or "\\" in raw:
        raise ValueError("Provider base URL contains invalid characters.")

    try:
        parts = urlsplit(raw)
        port = parts.port
        hostname = parts.hostname
    except ValueError as exc:
        raise ValueError("Provider base URL is malformed.") from exc

    scheme = parts.scheme.lower()
    if scheme not in ("http", "https"):
        raise ValueError("Provider base URL must use http or https.")
    # Userinfo stays allowed for gateways behind basic auth; the checks below read
    # the parsed hostname, so http://api.openai.com@169.254.169.254/ is caught.
    if not hostname:
        raise ValueError("Provider base URL must contain a hostname.")

    hostname = hostname.rstrip(".")
    if _metadata_host(hostname) or _resolves_to_metadata(hostname, port, scheme):
        raise ValueError("Cloud metadata endpoints cannot be used as a provider base URL.")

    if os.environ.get(_BLOCK_PRIVATE_ENV) == "1":
        _reject_non_public(hostname, port, scheme)

    return raw.rstrip("/")

View on GitHub (pinned to 203007d190)

Solutions

  1. Check the port segment — it must be a decimal number in 0-65535.
  2. Include the scheme and slashes: 'https://host:8080/v1', not 'host:8080/v1'.
  3. Validate with a URL parser client-side before submitting.

Example fix

# before
validate_provider_base_url("https://api.example.com:v1")
# after
validate_provider_base_url("https://api.example.com/v1")
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlsplit
try:
    urlsplit(raw)._hostinfo  # raises ValueError on bad port before the API sees it
except ValueError:
    reject_early(raw)

Type guard

def parses_as_url(raw: str) -> bool:
    from urllib.parse import urlsplit
    try:
        urlsplit(raw).port
        return True
    except ValueError:
        return False

Prevention

When it happens

Trigger: Base URLs like 'https://host:notaport/v1' (non-numeric port), 'https://host:99999/' (out-of-range port), or other urlsplit-invalid syntax.

Common situations: 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.

Understand the failure class

Related errors


AI-assisted analysis of unslothai/unsloth@203007d190 (2026-08-15). Data as JSON: /api/errors/2c3302633c3977ba. Report an issue: GitHub.