roboflow/supervision · error · ValueError
Invalid URL {url}: {error}
Error message
Invalid URL {url}: {error} What it means
This is the catch-all wrapper: any ValueError or requests.RequestException raised while parsing/preparing the URL is re-raised as `ValueError(f"Invalid URL {url!r}: {error}")` with the original error chained. It surfaces malformed URLs — control characters, invalid ports, spaces, unparseable syntax — that `urllib.parse` or `requests.Request.prepare()` reject during normalization.
Source
Thrown at src/supervision/utils/file.py:41
Returns:
Normalized URL string.
Raises:
ValueError: If the URL is invalid or uses an unsupported scheme.
"""
try:
original_parsed_url = urllib.parse.urlparse(url)
if "\\" in original_parsed_url.netloc:
raise ValueError("URL authority contains a backslash")
prepared_request = requests.Request(method="GET", url=url).prepare()
prepared_url = prepared_request.url
if prepared_url is None:
raise ValueError("prepared URL is empty")
parsed_url = urllib.parse.urlparse(prepared_url)
except (requests.RequestException, ValueError) as error:
raise ValueError(f"Invalid URL {url!r}: {error}") from error
if parsed_url.scheme not in {"http", "https"}:
raise ValueError(
f"Unsupported URL scheme {parsed_url.scheme!r} in {url!r}. "
"Only HTTP and HTTPS URLs are supported."
)
if parsed_url.hostname is None:
raise ValueError(f"Invalid URL {url!r}: no host supplied.")
return prepared_url
def _download_to_file(
url: str, target: Path, *, timeout: float = 30.0, stream: bool = False
) -> None:
"""
Download `url` to `target` atomically using a temporary file and `os.replace`.
View on GitHub (pinned to 7f254d9784)
Solutions
- Encode path/query components: `urllib.parse.quote(filename, safe='')` when building URLs.
- Print the chained original error (`except ValueError as e: print(e.__cause__)`) to see the exact parse failure.
- Validate user-supplied URLs with a strict regex or `urllib.parse.urlparse` sanity checks before use.
Example fix
# before
url = f'https://host/images/{filename}' # filename='my file.png' -> invalid
# after
from urllib.parse import quote
url = f'https://host/images/{quote(filename)}' # 'my%20file.png' Defensive patterns
Strategy: try-catch
Validate before calling
from urllib.parse import urlparse, quote
# build safely, encode user-supplied parts
url = f'https://host/images/{quote(filename, safe="")}'
parts = urlparse(url)
assert parts.scheme in {'http', 'https'} and parts.hostname Try / catch
try:
path = download_asset(url)
except ValueError as e:
log.error('bad URL %s: %s (cause: %s)', url, e, e.__cause__)
raise Prevention
- Percent-encode all user-supplied path/query components.
- Inspect e.__cause__ to find the underlying parse failure.
- Run urlparse sanity checks on external input before download.
When it happens
Trigger: URLs containing spaces or control characters (`'https://host/a b.png'`); invalid port (`'https://host:99999/'`); unparseable fragments; any requests-side InvalidURL/MissingSchema raised during preparation.
Common situations: Unencoded filenames in URLs (spaces, `#`, unicode); URLs assembled from user input without `urllib.parse.quote`; proxies/intermediaries mangling URLs; copy-paste with invisible characters.
Related errors
- Unsupported URL scheme {parsed_url.scheme} in {url}. Only HT
- Invalid URL {url}: no host supplied.
- prepared URL is empty
- Data pointed by URL could not be decoded into image.
- URL authority contains a backslash
AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15).
Data as JSON: /api/errors/aa8078e88d5b7768.
Report an issue: GitHub.