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

  1. Pass a fully-qualified URL: scheme + host + path, e.g. 'https://example.com/api'.
  2. Use yarl.URL to build URLs: base_url.with_path('/api').
  3. Validate that the URL has a host before sending: assert parsed.host.
  4. 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

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


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/862615f8d3fea0fd.json. Report an issue: GitHub.