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

  1. Encode path/query components: `urllib.parse.quote(filename, safe='')` when building URLs.
  2. Print the chained original error (`except ValueError as e: print(e.__cause__)`) to see the exact parse failure.
  3. 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

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


AI-assisted analysis of roboflow/supervision@7f254d9784 (2026-08-15). Data as JSON: /api/errors/aa8078e88d5b7768. Report an issue: GitHub.