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

  1. Remove credentials from the URL and keep only the Authorization header (`https://host/path` + `headers={'Authorization': ...}`).
  2. Or remove the Authorization header and let URL-embedded basic auth populate it (`https://user:pass@host`).
  3. 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

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


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