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

Raised by encode_basic_auth when the login contains a colon. RFC 7617 section 2 defines the user-id as everything before the first colon of the 'user:pass' credential string, so an embedded colon in the login makes the encoding ambiguous and aiohttp rejects it with ValueError before base64-encoding.

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 c0ef574e29)

Solutions

  1. Remove the colon from the login or URL-encode the username portion.
  2. Split 'user:pass' strings yourself instead of passing the whole thing as login.
  3. Use BasicAuth(login, password) with separate arguments, never embedding ':' in login.

Example fix

// before
auth = aiohttp.BasicAuth('john:doe', 'secret')
// after
auth = aiohttp.BasicAuth('john', 'secret')
Defensive patterns

Strategy: validation

Validate before calling

def split_basic_auth(s):
    login, _, password = s.partition(':')
    if ':' in login:
        raise ValueError('colon in login')
    return login, password

Type guard

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

Prevention

When it happens

Trigger: Calling aiohttp.BasicAuth('user:name', 'pass') or encode_basic_auth('a:b'). Also indirectly via strip_auth_from_url when a URL embeds a user with a colon (URLs forbid it but crafted input can).

Common situations: Email-style logins used as HTTP usernames; concatenating domain\\user incorrectly; config sourced from upstream that includes colons.

Related errors


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