{"id":"862615f8d3fea0fd","repo":"aio-libs/aiohttp","slug":"url","errorCode":null,"errorMessage":"{url}","messagePattern":"\\{url\\}","errorType":"exception","errorClass":"InvalidURL","httpStatus":null,"severity":"error","filePath":"aiohttp/client_reqrep.py","lineNumber":843,"sourceCode":"    ):\n        if match := _CONTAINS_CONTROL_CHAR_RE.search(method):\n            raise ValueError(\n                f\"Method cannot contain non-token characters {method!r} \"\n                f\"(found at least {match.group()!r})\"\n            )\n        # URL forbids subclasses, so a simple type check is enough.\n        assert type(url) is URL, url\n        self.original_url = url\n        self.url = url.with_fragment(None) if url.raw_fragment else url\n        self.method = method.upper()\n        self.loop = loop\n        self._ssl = ssl\n\n        if loop.get_debug():\n            self._source_traceback = traceback.extract_stack(sys._getframe(1))\n\n        if not url.raw_host:\n            raise InvalidURL(url)\n        self._update_headers(headers)\n        if url.raw_user or url.raw_password:\n            self.headers[hdrs.AUTHORIZATION] = encode_basic_auth(\n                url.user or \"\", url.password or \"\"\n            )\n\n    def _reset_writer(self, _: object = None) -> None:\n        self._writer_task = None\n\n    def _get_content_length(self) -> int | None:\n        \"\"\"Extract and validate Content-Length header value.\n\n        Returns parsed Content-Length value or None if not set.\n        Raises ValueError if header exists but cannot be parsed as an integer.\n        \"\"\"\n        if hdrs.CONTENT_LENGTH not in self.headers:\n            return None\n","sourceCodeStart":825,"sourceCodeEnd":861,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/client_reqrep.py#L825-L861","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nawait session.get('/api/users')           # no host\nawait session.get(env_base_url + path)   # env_base_url empty\n# after\nawait session.get('https://example.com/api/users')\n# or\nbase = yarl.URL(BASE_URL or 'https://default.example.com')\nawait session.get(str(base / 'api' / 'users'))","handlingStrategy":"validation","validationCode":"from yarl import URL\nu = URL(url)\nif not u.raw_host:\n    raise ValueError(f'URL missing host: {url!r}')\nawait session.get(u)","typeGuard":"def has_host(url) -> bool:\n    from yarl import URL\n    return bool(URL(url).raw_host)","tryCatchPattern":"from aiohttp import InvalidURL\ntry:\n    await session.get(url)\nexcept InvalidURL as e:\n    # log and surface a clearer error to the caller","preventionTips":["Always pass absolute URLs (scheme + host).","Build URLs with yarl.URL from a validated base.","Validate env-supplied base URLs at startup."],"tags":["url","validation","http-client","configuration"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}