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
- Ensure path begins with '/' when an authority is present: path = '/' + path if path and not path.startswith('/') else path.
- Use httpx.URL(...) and then url.copy_with(path=normalized) rather than concatenation.
- Validate with: if host and path and not path.startswith('/'): raise.
- 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
- Always prefix user-supplied paths with '/' when joining to a host.
- Prefer urllib.parse.urljoin over string concatenation.
- Assert path invariants in URL-builder unit tests.
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
- Relative URLs cannot have a path starting with '//'
- Relative URLs cannot have a path starting with ':'
- URL too long
- Invalid non-printable ASCII character in URL, {char!r} at po
- URL component '{key}' too long
AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04).
Data as JSON: /data/errors/f2aa31ed9a2cc2cf.json.
Report an issue: GitHub.