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, no authority) starts with ':'. RFC 3986 §4.2 forbids the first path segment of a relative-path reference from containing a colon, because ':' would be ambiguous with a scheme delimiter. httpx rejects a leading ':' outright.

Source

Thrown at httpx/_urlparse.py:444

    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

    components = path.split("/")

    # Fast return when no '.' or '..' components in the path.
    if "." not in components and ".." not in components:

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Prefix the path with './' to disambiguate: httpx.URL('./:foo').
  2. Supply a scheme so the URL is absolute.
  3. Strip or reject leading colons in user-supplied relative paths.
  4. Use urljoin against a base URL rather than constructing relative URLs by hand.

Example fix

// before
url = httpx.URL(":foo")  # InvalidURL

// after
url = httpx.URL("./:foo")  # or provide a full URL
Defensive patterns

Strategy: validation

Validate before calling

def disambiguate_relative(path: str) -> str:
    if path.startswith(":"):
        return "./" + path
    return path

url = httpx.URL(disambiguate_relative(user_path))

Type guard

def relative_path_is_safe(path: str) -> bool:
    return not path.startswith(":")

Try / catch

from httpx import InvalidURL

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

Prevention

When it happens

Trigger: httpx.URL(':foo'), httpx.URL(path=':bar'), or constructing a relative reference where the path begins with a colon.

Common situations: Building a relative URL that accidentally begins with ':' (e.g. from a templated value); user input starting with ':'; mis-formed link extraction.

Related errors


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