{"record":{"id":"54d0cef399d2ab1a","repo":"sgl-project/sglang","slug":"media-url-timeout-must-be-positive","errorCode":null,"errorMessage":"media URL timeout must be positive","messagePattern":"media URL timeout must be positive","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/sglang/srt/utils/common.py","lineNumber":1599,"sourceCode":"    if _allowed_media_domains and hostname not in _allowed_media_domains:\n        raise ValueError(\n            \"Media URL domain is not allowed. \"\n            f\"Allowed domains: {sorted(_allowed_media_domains)}; \"\n            f\"input domain: {hostname}\"\n        )\n\n\ndef download_remote_media(url: str, timeout: float) -> bytes:\n    \"\"\"Download one HTTP(S) media object under the configured URL policy.\n\n    Redirects are followed manually so every destination is validated before\n    a connection is made. The response is streamed to enforce both the total\n    request deadline and the configured byte limit without first buffering an\n    attacker-controlled body in memory.\n    \"\"\"\n\n    if timeout <= 0:\n        raise ValueError(\"media URL timeout must be positive\")\n\n    session = get_mm_http_session()\n    deadline = time.monotonic() + timeout\n    current_url = url\n\n    for redirect_count in range(_MAX_MEDIA_URL_REDIRECTS + 1):\n        # Validate the same normalized URL representation that requests sends\n        # to urllib3. This avoids parser disagreements around backslashes and\n        # userinfo separators.\n        prepared_url = requests.Request(\"GET\", current_url).prepare().url\n        if prepared_url is None:\n            raise ValueError(f\"Invalid media URL: {current_url!r}\")\n        _assert_media_url_allowed(prepared_url)\n\n        remaining = deadline - time.monotonic()\n        if remaining <= 0:\n            raise requests.exceptions.Timeout(\n                f\"Timed out while downloading media URL: {url}\"","sourceCodeStart":1581,"sourceCodeEnd":1617,"githubUrl":"https://github.com/sgl-project/sglang/blob/0132848349585cfe6aae51c4941cbae872505f8a/python/sglang/srt/utils/common.py#L1581-L1617","documentation":"download_remote_media requires a strictly positive timeout (seconds) that bounds the whole download including redirects. Zero or negative timeouts are rejected because the deadline-based loop (time.monotonic() + timeout) would immediately expire and the semantics would be undefined.","triggerScenarios":"Calling download_remote_media(url, timeout=0) or with a negative value; often when a caller forwards a user/config-supplied timeout that defaults to 0 or was computed as (deadline - now) <= 0.","commonSituations":"Passing None-handling code that coerces to 0; copying a per-request timeout of 0 meaning 'no wait'; misconfigured SGLANG_MEDIA_URL_TIMEOUT-style config.","solutions":["Pass a positive timeout such as 10 or 30 seconds","Default the value when unset: timeout = timeout or _DEFAULT_TIMEOUT","Clamp: timeout = max(1, timeout)"],"exampleFix":"# before\ndata = download_remote_media(url, timeout=0)\n# after\ndata = download_remote_media(url, timeout=max(1, int(timeout or 10)))","handlingStrategy":"validation","validationCode":"timeout = timeout if isinstance(timeout, (int, float)) and timeout > 0 else 30\ndata = download_remote_media(url, timeout=timeout)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Default timeouts with `timeout or DEFAULT`","Never forward 0-as-none semantics into this API"],"tags":["validation","media","timeout","network"],"backgroundTag":"invalid-argument-value","analyzedSha":"0132848349585cfe6aae51c4941cbae872505f8a","analyzedAt":"2026-08-28T05:10:05.995Z","schemaVersion":2},"datasetVersion":"2026-08-28T06:17:29.519Z"}