aio-libs/aiohttp · error · ClientError
Malformed Digest auth challenge: Missing 'nonce' parameter
Error message
Malformed Digest auth challenge: Missing 'nonce' parameter
What it means
Raised as `ClientError` in `DigestAuthMiddleware._encode` (client_middleware_digest_auth.py:247-250) when the parsed challenge lacks a `nonce` key. The server's nonce is essential to Digest auth: it's part of H(A1)/H(A2), the client counter (`nc`) is tracked against it, and it's replay-protection material (client_middleware_digest_auth.py:215-216). A separate guard at line 257-260 also rejects an *empty* nonce string for the same security reason.
Source
Thrown at aiohttp/client_middleware_digest_auth.py:248
url: The request URL
body: The request body (used for qop=auth-int)
Returns:
A fully formatted Digest authorization header string
Raises:
ClientError: If the challenge is missing required parameters or
contains unsupported values
"""
challenge = self._challenge
if "realm" not in challenge:
raise ClientError(
"Malformed Digest auth challenge: Missing 'realm' parameter"
)
if "nonce" not in challenge:
raise ClientError(
"Malformed Digest auth challenge: Missing 'nonce' parameter"
)
# Empty realm values are allowed per RFC 7616 (SHOULD, not MUST, contain host name)
realm = challenge["realm"]
nonce = challenge["nonce"]
# Empty nonce values are not allowed as they are security-critical for replay protection
if not nonce:
raise ClientError(
"Security issue: Digest auth challenge contains empty 'nonce' value"
)
qop_raw = challenge.get("qop", "")
# Preserve original algorithm case for response while using uppercase for processing
algorithm_original = challenge.get("algorithm", "MD5")
algorithm = algorithm_original.upper()
opaque = challenge.get("opaque", "")View on GitHub (pinned to c0ef574e29)
Solutions
- Capture and inspect the raw `WWW-Authenticate` header.
- Fix the server to include a non-empty `nonce=` in the Digest challenge.
- Ensure the challenge is well-formed (proper quoting: `nonce="abc123"`).
- Catch `ClientError` and surface a clear auth-failure rather than retrying.
Example fix
// before # server sends: WWW-Authenticate: Digest realm="r" (no nonce) # -> ClientError: Missing 'nonce' // after (server-side) # WWW-Authenticate: Digest realm="r", nonce="opaque-random-value", qop="auth"
Defensive patterns
Strategy: try-catch
Validate before calling
from aiohttp import hdrs
def challenge_has_nonce(resp_headers) -> bool:
auth = resp_headers.get(hdrs.WWW_AUTHENTICATE, '')
return 'nonce=' in auth.lower() Try / catch
from aiohttp import ClientError
try:
resp = await session.get(url, middlewares=[digest_mw])
except ClientError as e:
if "Missing 'nonce'" in str(e):
log.error('server Digest challenge lacks nonce; check WWW-Authenticate')
raise Prevention
- Ensure the server's Digest challenge includes a non-empty `nonce`.
- Treat a missing/empty nonce as a server-side defect, not a client bug.
- Log 401 response headers to audit challenge completeness.
When it happens
Trigger: Server returns `WWW-Authenticate: Digest realm="x"` with no `nonce=`; the parser failed to extract nonce due to quoting/format issues; nonce field is present but empty.
Common situations: Server misconfiguration; buggy auth server omitting nonce; intermediary stripping challenge parameters; a Digest challenge from a server that doesn't fully implement RFC 7616.
Related errors
- Malformed Digest auth challenge: Missing 'realm' parameter
- A ":" is not allowed in username (RFC 1945#section-11.1)
- Cannot combine AUTHORIZATION header with credentials encoded
- None is not allowed as login value
- None is not allowed as password value
AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04).
Data as JSON: /data/errors/511f23d191ead1fd.json.
Report an issue: GitHub.