aio-libs/aiohttp · error · ValueError
Cannot combine AUTHORIZATION header with credentials…
Error message
Cannot combine AUTHORIZATION header with credentials encoded in URL
What it means
Raised inside the request loop on the initial request (history is empty) when the URL contains userinfo (user:pass@host) AND an explicit Authorization header is present. aiohttp refuses to silently let URL credentials shadow a header the caller set, since that is a classic source of auth-bypass and credential-leak bugs.
Solutions
- Remove credentials from the URL and keep only the Authorization header.
- Or remove the Authorization header and let the URL's userinfo drive Basic auth.
- If using a session-level Authorization header, strip userinfo from request URLs at build time.
Example fix
// before
await session.get('https://user:pass@host/path', headers={'Authorization': 'Bearer x'})
// after
await session.get('https://host/path', headers={'Authorization': 'Bearer x'}) Defensive patterns
Strategy: validation
Validate before calling
from aiohttp import URL
from multidict import CIMultiDict
def strip_url_userinfo_if_auth_header(url, headers):
if any(k.lower() == 'authorization' for k in headers):
u = URL(url)
if u.user is not None:
url = u.with_user(None).with_password(None)
return url Type guard
from aiohttp import URL
def url_has_userinfo(u) -> bool:
u = URL(u)
return u.user is not None Try / catch
try:
resp = await session.get(url, headers=headers)
except ValueError as e:
if 'AUTHORIZATION' in str(e):
from aiohttp import URL
u = URL(url)
resp = await session.get(str(u.with_user(None).with_password(None)), headers=headers)
else:
raise Prevention
- Never embed credentials in URLs; use BasicAuth or explicit headers.
- Build URLs from typed components instead of concatenating strings.
- Strip userinfo at the URL construction boundary if Authorization headers are used.
When it happens
Trigger: Calling session.get('https://user:pass@host/path', headers={'Authorization': 'Bearer x'}). Also when default headers on the session include Authorization and the URL has embedded credentials.
Common situations: Copy-pasting a URL with credentials from a deploy dashboard while also adding a token header. Session-level default Authorization header combined with a per-request URL that carries basic auth. Migrating from a lib that silently preferred one source.
Related errors
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/d6f1c55ee555b43d.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/client.py:629
while True:
url, auth_from_url = strip_auth_from_url(url)
if not url.raw_host:
# NOTE: Bail early, otherwise, causes `InvalidURL` through
# NOTE: `self._request_class()` below.
err_exc_cls = (
InvalidUrlRedirectClientError
if redirects
else InvalidUrlClientError
)
raise err_exc_cls(url)
if auth_from_url is not None:
# URL-embedded credentials override any Authorization
# header already present (e.g. carried from a previous
# redirect). On the initial request, refuse to silently
# shadow an explicit Authorization header.
if not history and hdrs.AUTHORIZATION in headers:
raise ValueError(
"Cannot combine AUTHORIZATION header with "
"credentials encoded in URL"
)
headers[hdrs.AUTHORIZATION] = auth_from_url
elif (
self._trust_env
and url.host is not None
and hdrs.AUTHORIZATION not in headers
):
# Fall back to ~/.netrc credentials when trust_env is set.
netrc_auth = await self._loop.run_in_executor(
None, self._get_netrc_auth, url.host
)
if netrc_auth is not None:
headers[hdrs.AUTHORIZATION] = netrc_auth
all_cookies = self._cookie_jar.filter_cookies(url)
View on GitHub (pinned to d041d4d0fd)