sgl-project/sglang · error · ValueError

Invalid media URL: {url!r}

Error message

Invalid media URL: {url!r}

What it means

download_remote_media's URL gate (_assert_media_url_allowed) only accepts http/https URLs with a parseable hostname. FTP/data/file scheme URLs, or relative/garbage strings with no hostname, are rejected before any network request to prevent SSRF and unsupported-protocol issues.

Source

Thrown at python/sglang/srt/utils/common.py:1578

    checks apply to image, video, audio, cache, and model-specific loaders.
    """

    if max_file_size_mb < 0:
        raise ValueError("media_url_max_file_size_mb must be non-negative")

    normalized_domains = sorted(
        {_normalize_media_domain(domain) for domain in allowed_media_domains or []}
    )
    global _allowed_media_domains, _media_url_max_file_size_bytes
    _allowed_media_domains = frozenset(normalized_domains)
    _media_url_max_file_size_bytes = max_file_size_mb * 1024 * 1024
    return normalized_domains


def _assert_media_url_allowed(url: str) -> None:
    parsed = urlparse(url)
    if parsed.scheme not in {"http", "https"} or parsed.hostname is None:
        raise ValueError(f"Invalid media URL: {url!r}")

    hostname = _normalize_media_domain(parsed.hostname)
    if _allowed_media_domains and hostname not in _allowed_media_domains:
        raise ValueError(
            "Media URL domain is not allowed. "
            f"Allowed domains: {sorted(_allowed_media_domains)}; "
            f"input domain: {hostname}"
        )


def download_remote_media(url: str, timeout: float) -> bytes:
    """Download one HTTP(S) media object under the configured URL policy.

    Redirects are followed manually so every destination is validated before
    a connection is made. The response is streamed to enforce both the total
    request deadline and the configured byte limit without first buffering an
    attacker-controlled body in memory.
    """

View on GitHub (pinned to 0132848349)

Solutions

  1. Handle local paths and base64/data URIs before calling download_remote_media
  2. Reject or URL-decode user input into the expected http(s) form client-side
  3. Keep the allowlist configured so accidental non-http inputs are caught

Example fix

# before
media = download_remote_media('data:image/png;base64,iVBOR...')
# after
if image.startswith('data:'): data = pybase64.b64decode(image.split(',', 1)[1])
else: data = download_remote_media(image)
Defensive patterns

Strategy: validation

Validate before calling

from urllib.parse import urlparse
p = urlparse(url)
assert p.scheme in ('http', 'https') and p.hostname, f'bad media url {url!r}'
data = download_remote_media(url, timeout=30)

Type guard

def is_http_url(v) -> bool:
    if not isinstance(v, str): return False
    p = urlparse(v)
    return p.scheme in {'http', 'https'} and p.hostname is not None

Prevention

When it happens

Trigger: Passing 'ftp://x/file.png', 'file:///tmp/a.jpg', 'data:image/png;base64,...', or 'not-a-url' to download_remote_media (via fetch_image, preprocess_audio, load_audio, etc.) when it is not routed to a local/base64 path first.

Common situations: Multimodal pipelines forwarding arbitrary user-supplied strings to the remote downloader; base64 payloads not being detected earlier; clients sending file:// paths hoping to read server-local files (SSRF/LFI attempt).

Related errors


AI-assisted analysis of sgl-project/sglang@0132848349 (2026-08-28). Data as JSON: /api/errors/00b816b3e4a75dd2. Report an issue: GitHub.