encode/httpx · error · InvalidURL

Relative URLs cannot have a path starting with '//'

Error message

Relative URLs cannot have a path starting with '//'

What it means

Raised by validate_path when a relative URL (no scheme and no authority) has a path beginning with '//'. Per RFC 3986 §4.2, '//path' would be parsed as an authority, leaving an empty host; rather than silently mis-parsing, httpx rejects it. A leading '//' on a scheme-less URL is almost always a typo or a missing scheme.

Source

Thrown at httpx/_urlparse.py:439

def validate_path(path: str, has_scheme: bool, has_authority: bool) -> None:
    """
    Path validation rules that depend on if the URL contains
    a scheme or authority component.

    See https://datatracker.ietf.org/doc/html/rfc3986.html#section-3.3
    """
    if has_authority:
        # If a URI contains an authority component, then the path component
        # must either be empty or begin with a slash ("/") character."
        if path and not path.startswith("/"):
            raise InvalidURL("For absolute URLs, path must be empty or begin with '/'")

    if not has_scheme and not has_authority:
        # If a URI does not contain an authority component, then the path cannot begin
        # with two slash characters ("//").
        if path.startswith("//"):
            raise InvalidURL("Relative URLs cannot have a path starting with '//'")

        # In addition, a URI reference (Section 4.1) may be a relative-path reference,
        # in which case the first path segment cannot contain a colon (":") character.
        if path.startswith(":"):
            raise InvalidURL("Relative URLs cannot have a path starting with ':'")


def normalize_path(path: str) -> str:
    """
    Drop "." and ".." segments from a URL path.

    For example:

        normalize_path("/path/./to/somewhere/..") == "/path/to"
    """
    # Fast return when no '.' characters in the path.
    if "." not in path:
        return path

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Provide a scheme: httpx.URL('https://example.com/path').
  2. If the URL is intentionally relative, prefix the path with a single '/' or remove the leading slashes.
  3. Detect protocol-relative strings at input time and prepend a default scheme.
  4. Use urljoin with a base URL to resolve relative references.

Example fix

// before
url = httpx.URL("//example.com/path")  # InvalidURL

// after
url = httpx.URL("https://example.com/path")
Defensive patterns

Strategy: validation

Validate before calling

def absolutize(url: str, default_scheme: str = "https") -> str:
    if url.startswith("//"):
        return f"{default_scheme}:{url}"
    return url

url = httpx.URL(absolutize(user_url))

Type guard

def is_protocol_relative(url: str) -> bool:
    return url.startswith("//") and not url.startswith("///")

Try / catch

from httpx import InvalidURL

try:
    url = httpx.URL(maybe_relative)
except InvalidURL as e:
    if "cannot have a path starting with '//'" in str(e):
        url = httpx.URL("https:" + maybe_relative)
    else:
        raise

Prevention

When it happens

Trigger: httpx.URL('//example.com/path') (no scheme), or httpx.URL(path='//foo').

Common situations: Dropping the 'https:' from a copied URL but keeping '//'; building protocol-relative URLs as httpx.URL objects (they are only valid in HTML context, not as a request target); missing scheme when concatenating.

Related errors


AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04). Data as JSON: /data/errors/ca47ea2b3fbbf04a.json. Report an issue: GitHub.