aio-libs/aiohttp · error · ValueError
A ":" is not allowed in username (RFC 1945#section-11.1)
Error message
A ":" is not allowed in username (RFC 1945#section-11.1)
What it means
Raised by DigestAuthMiddleware.__init__ when login contains a ':' character. RFC 1945 section 11.1 forbids ':' in digest usernames because it is the delimiter of the username realm fields in the Authorization header; including it would corrupt the header grammar.
Solutions
- Remove the ':' from the username (e.g. use 'domain\\user' or 'user@realm' if the server accepts it).
- If you constructed the value by joining fields with ':', switch the separator to '.' or '_'.
- Verify against the server's accepted username format (often just the bare sAMAccountName).
Example fix
// before mw = DigestAuthMiddleware(login='corp:jdoe', password='x') // after mw = DigestAuthMiddleware(login='jdoe', password='x')
Defensive patterns
Strategy: validation
Validate before calling
def sanitize_login(login: str) -> str:
if ':' in login:
raise ValueError('login must not contain ":" per RFC 1945')
return login
DigestAuthMiddleware(login=sanitize_login(login), password=password) Type guard
def is_rfc1945_login(v: str) -> bool:
return isinstance(v, str) and ':' not in v Try / catch
try:
mw = DigestAuthMiddleware(login=login, password=password)
except ValueError as e:
if ':' in str(e) or 'RFC 1945' in str(e):
login = login.replace(':', '_')
mw = DigestAuthMiddleware(login=login, password=password)
else:
raise Prevention
- Build usernames from typed fields, never by joining with ':'.
- Validate credential format at the config boundary, not in the auth library.
- Document the RFC 1945 username restriction for ops teams supplying credentials.
When it happens
Trigger: Constructing DigestAuthMiddleware(login='domain\\user:extra', password='x') or any login value that contains a colon. The ':' in login check fires before any encoding.
Common situations: AD-style 'domain\\user' formatted with a colon by mistake. Email-style 'user:realm' usernames. Concatenating realm and username with ':'.
Related errors
- None is not allowed as login value
- None is not allowed as password value
- A ":" is not allowed in login (RFC 7617#section-2)
- Digest auth error: Unsupported hash algorithm
- Digest auth error: Unsupported Quality of Protection (qop)…
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/ff712a03e0b3fa90.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/client_middleware_digest_auth.py:209
The core digest calculation is inspired by the implementation in
https://github.com/requests/requests/blob/v2.18.4/requests/auth.py
with added support for modern digest auth features and error handling.
"""
def __init__(
self,
login: str,
password: str,
preemptive: bool = True,
) -> None:
if login is None:
raise ValueError("None is not allowed as login value")
if password is None:
raise ValueError("None is not allowed as password value")
if ":" in login:
raise ValueError('A ":" is not allowed in username (RFC 1945#section-11.1)')
self._login_str: Final[str] = login
self._login_bytes: Final[bytes] = login.encode("utf-8")
self._password_bytes: Final[bytes] = password.encode("utf-8")
self._last_nonce_bytes = b""
self._nonce_count = 0
self._challenge: DigestAuthChallenge = {}
self._preemptive: bool = preemptive
# Set of URLs defining the protection space
self._protection_space: list[str] = []
# Origin the credentials are scoped to; set on the first request.
self._origin: URL | None = None
async def _encode(self, method: str, url: URL, body: Payload | Literal[b""]) -> str:
"""
Build digest authorization header for the current challenge.
View on GitHub (pinned to d041d4d0fd)