aio-libs/aiohttp · error · ValueError

A ":" is not allowed in login (RFC 7617#section-2)

Error message

A ":" is not allowed in login (RFC 7617#section-2)

What it means

encode_basic_auth raises ValueError when the login (username) string contains a colon character. Per RFC 7617 Section 2, the colon is the delimiter between userid and password in the Basic credentials, so it cannot appear in the userid itself. This is a hard validation, not a warning.

Solutions

  1. Remove or replace the colon in the login: use a sanitized username
  2. URL-encode the colon in the userinfo: replace ':' with '%3A' in the URL (yarl may handle this)
  3. Pass credentials via the auth parameter instead of embedding in the URL: ClientSession(auth=BasicAuth('user', 'pass'))

Example fix

# before
await session.get('http://user:name@host/api')

# after
from aiohttp import BasicAuth
await session.get('http://host/api', auth=BasicAuth('user', 'password'))
Defensive patterns

Strategy: validation

Validate before calling

def safe_basic_auth(login, password='', encoding='utf-8'):
    if ':' in login:
        raise ValueError(f'Login contains illegal colon: {login!r}')
    from aiohttp.helpers import encode_basic_auth
    return encode_basic_auth(login, password, encoding)

Type guard

def is_valid_basic_auth_login(login: str) -> bool:
    return isinstance(login, str) and ':' not in login

Try / catch

try:
    from aiohttp.helpers import encode_basic_auth
    header = encode_basic_auth(login, password)
except ValueError as e:
    if 'not allowed in login' in str(e):
        login = login.replace(':', '')
        header = encode_basic_auth(login, password)
    raise

Prevention

When it happens

Trigger: Calling aiohttp.helpers.encode_basic_auth(login='user:name', password='secret'), or providing a URL with userinfo like http://user:name@host/. Also triggered indirectly when aiohttp encodes credentials from a URL or from BasicAuth(login, password) where login contains ':'.

Common situations: Email addresses used as usernames ('user@example.com' is fine, but 'user:name' is not); Active Directory UPN with colons; copy-pasted credentials from a config that uses colon-delimited format; URLs with encoded or raw colons in the userinfo component.

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/beb984c6995c79a0. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/helpers.py:167

    "{",
    "}",
    " ",
    chr(9),
}
TOKEN = CHAR ^ CTL ^ SEPARATORS


json_re = re.compile(r"^(?:application/|[\w.-]+/[\w.+-]+?\+)json$", re.IGNORECASE)


def encode_basic_auth(login: str, password: str = "", encoding: str = "utf-8") -> str:
    """Encode HTTP Basic Authentication credentials as an Authorization header value.

    Returns a string of the form ``"Basic <base64>"`` suitable for use as the
    value of the ``Authorization`` (or ``Proxy-Authorization``) header.
    """
    if ":" in login:
        raise ValueError('A ":" is not allowed in login (RFC 7617#section-2)')
    creds = f"{login}:{password}".encode(encoding)
    return "Basic " + base64.b64encode(creds).decode(encoding)


def strip_auth_from_url(url: URL) -> tuple[URL, str | None]:
    """Strip user/password from a URL and return the Authorization header value.

    Returns a tuple of ``(url_without_credentials, authorization_header_value)``.
    The header value is ``None`` if no credentials were present.
    """
    # Check raw_user and raw_password first as yarl is likely
    # to already have these values parsed from the netloc in the cache.
    if url.raw_user is None and url.raw_password is None:
        return url, None
    return url.with_user(None), encode_basic_auth(url.user or "", url.password or "")


def netrc_from_env() -> netrc.netrc | None:

View on GitHub (pinned to d041d4d0fd)