aio-libs/aiohttp · error · ValueError
Cannot combine AUTHORIZATION header with credentials encoded
Error message
Cannot combine AUTHORIZATION header with credentials encoded in URL
What it means
Raised in `_request` (client.py:623-632) on the *initial* request (`not history`) when the URL contains userinfo (`user:pass@host`) AND the caller also set an explicit `Authorization` header. aiohttp refuses to silently shadow a hand-set Authorization header with URL-embedded credentials — a deliberate fail-fast to prevent credential confusion. On redirects (`history` non-empty), URL credentials override silently, which is why the guard is gated on `not history`.
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 c0ef574e29)
Solutions
- Remove credentials from the URL and keep only the Authorization header (`https://host/path` + `headers={'Authorization': ...}`).
- Or remove the Authorization header and let URL-embedded basic auth populate it (`https://user:pass@host`).
- Avoid putting credentials in URLs entirely — they leak into logs, proxies, and Referer.
Example fix
// before
await session.get('https://u:p@host/api', headers={'Authorization': 'Bearer t'})
// after
await session.get('https://host/api', headers={'Authorization': 'Bearer t'}) Defensive patterns
Strategy: validation
Validate before calling
from yarl import URL
def strip_url_credentials(url, headers):
u = URL(url)
if u.user is not None and 'Authorization' in (headers or {}):
raise ValueError('Cannot combine AUTHORIZATION header with URL credentials')
return str(u.with_user(None).with_password(None)) if u.user else url Prevention
- Never embed credentials in URLs — they leak into logs and proxies.
- Pick one auth mechanism: either an Authorization header or URL userinfo, not both.
- Sanitize URLs from config/untrusted sources with yarl's `.with_user(None)`.
When it happens
Trigger: `session.get('https://user:pass@host/path', headers={'Authorization': 'Bearer x'})`. Also via netrc auto-credentials combined with a URL that has its own userinfo, or via a redirect-captured Authorization header reaching a URL with embedded creds.
Common situations: Mixing bearer-token auth with a legacy URL that still has basic-auth creds baked in; copy-pasting a URL from a browser that exposed credentials; CI configs that put secrets in the URL and also pass a header.
Related errors
- Malformed Digest auth challenge: Missing 'nonce' parameter
- base_url must have a trailing '/'
- Server attempted redirecting to a location that does not loo
- Invalid redirect URL origin
- None is not allowed as login value
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/d6f1c55ee555b43d.json.
Report an issue: GitHub.