encode/httpx · error · InvalidURL

For absolute URLs, path must be empty or begin with '/'

Error message

For absolute URLs, path must be empty or begin with '/'

What it means

Raised by validate_path when the URL has an authority (host) but the path is non-empty and does not start with '/'. Per RFC 3986 §3.3, an absolute URI with an authority component must have a path that is empty or begins with '/'. A path like 'foo' after a host is ambiguous and rejected.

Source

Thrown at httpx/_urlparse.py:433

        scheme
    )
    if port_as_int == default_port:
        return None
    return port_as_int


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:

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Ensure path begins with '/' when an authority is present: path = '/' + path if path and not path.startswith('/') else path.
  2. Use httpx.URL(...) and then url.copy_with(path=normalized) rather than concatenation.
  3. Validate with: if host and path and not path.startswith('/'): raise.
  4. Prefer urljoin('https://example.com/', 'foo') which inserts the slash.

Example fix

// before
url = httpx.URL(scheme="https", host="example.com", path="items/1")  # InvalidURL

// after
url = httpx.URL(scheme="https", host="example.com", path="/items/1")
Defensive patterns

Strategy: validation

Validate before calling

def join_path(host: str, path: str) -> str:
    if path and not path.startswith("/"):
        path = "/" + path
    return path

url = httpx.URL(scheme="https", host=host, path=join_path(host, user_path))

Type guard

def path_ok_for_absolute(path: str) -> bool:
    return not path or path.startswith("/")

Try / catch

from httpx import InvalidURL

try:
    url = httpx.URL(scheme="https", host="x", path=p)
except InvalidURL as e:
    if "must be empty or begin with '/'" in str(e):
        url = httpx.URL(scheme="https", host="x", path="/" + p)
    else:
        raise

Prevention

When it happens

Trigger: httpx.URL('https://example.comfoo') (missing slash), or building a URL with host='example.com' and path='foo' (no leading slash).

Common situations: String-concatenating a host and a path without a separating '/'; user-supplied path that omits the leading slash; copy-paste dropping the slash.

Related errors


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