aio-libs/aiohttp · error · InvalidURL
{url}
Error message
{url} What it means
Raised as InvalidURL (a ClientError/ValueError) in ClientRequest.__init__ when url.raw_host is falsy. aiohttp requires every request URL to have a host component; a URL without one (e.g. '/path', '', or 'http:///path') cannot be dispatched. The exception message is just the offending URL.
Source
Thrown at aiohttp/client_reqrep.py:843
):
if match := _CONTAINS_CONTROL_CHAR_RE.search(method):
raise ValueError(
f"Method cannot contain non-token characters {method!r} "
f"(found at least {match.group()!r})"
)
# URL forbids subclasses, so a simple type check is enough.
assert type(url) is URL, url
self.original_url = url
self.url = url.with_fragment(None) if url.raw_fragment else url
self.method = method.upper()
self.loop = loop
self._ssl = ssl
if loop.get_debug():
self._source_traceback = traceback.extract_stack(sys._getframe(1))
if not url.raw_host:
raise InvalidURL(url)
self._update_headers(headers)
if url.raw_user or url.raw_password:
self.headers[hdrs.AUTHORIZATION] = encode_basic_auth(
url.user or "", url.password or ""
)
def _reset_writer(self, _: object = None) -> None:
self._writer_task = None
def _get_content_length(self) -> int | None:
"""Extract and validate Content-Length header value.
Returns parsed Content-Length value or None if not set.
Raises ValueError if header exists but cannot be parsed as an integer.
"""
if hdrs.CONTENT_LENGTH not in self.headers:
return None
View on GitHub (pinned to c0ef574e29)
Solutions
- Pass a fully-qualified URL: scheme + host + path, e.g. 'https://example.com/api'.
- Use yarl.URL to build URLs: base_url.with_path('/api').
- Validate that the URL has a host before sending: assert parsed.host.
- Check environment variables supplying the base URL are non-empty.
Example fix
# before
await session.get('/api/users') # no host
await session.get(env_base_url + path) # env_base_url empty
# after
await session.get('https://example.com/api/users')
# or
base = yarl.URL(BASE_URL or 'https://default.example.com')
await session.get(str(base / 'api' / 'users')) Defensive patterns
Strategy: validation
Validate before calling
from yarl import URL
u = URL(url)
if not u.raw_host:
raise ValueError(f'URL missing host: {url!r}')
await session.get(u) Type guard
def has_host(url) -> bool:
from yarl import URL
return bool(URL(url).raw_host) Try / catch
from aiohttp import InvalidURL
try:
await session.get(url)
except InvalidURL as e:
# log and surface a clearer error to the caller Prevention
- Always pass absolute URLs (scheme + host).
- Build URLs with yarl.URL from a validated base.
- Validate env-supplied base URLs at startup.
When it happens
Trigger: Fires at line 842-843 when url.raw_host evaluates False. Triggered by passing a relative path string, an empty string, a URL with only a path, or a yarl.URL object whose host is None.
Common situations: Passing relative paths ('/api/users') instead of absolute URLs; constructing URLs by string concatenation that drops the scheme/host; env var for base URL being empty; redirects to a Location header without host.
Related errors
- base_url must have a trailing '/'
- Digest auth error: Unsupported Quality of Protection (qop) v
- total timeout must be a positive number or None to disable,
- aiohttp only supports http(s) proxies (got: {proxy.scheme!r}
- {host} - is not a canonical IPv4 address
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/862615f8d3fea0fd.json.
Report an issue: GitHub.