encode/httpx · error · ProtocolError
Malformed Digest WWW-Authenticate header
Error message
Malformed Digest WWW-Authenticate header
What it means
Raised as httpx.ProtocolError when DigestAuth._parse_challenge cannot find a required field. After a 401 with a 'WWW-Authenticate: Digest ...' header, httpx splits the header into key=value pairs and reads 'realm' and 'nonce' (KeyError on either triggers this). It indicates the server sent a Digest challenge missing RFC 7616 mandatory fields.
Source
Thrown at httpx/_auth.py:253
assert scheme.lower() == "digest"
header_dict: dict[str, str] = {}
for field in parse_http_list(fields):
key, value = field.strip().split("=", 1)
header_dict[key] = unquote(value)
try:
realm = header_dict["realm"].encode()
nonce = header_dict["nonce"].encode()
algorithm = header_dict.get("algorithm", "MD5")
opaque = header_dict["opaque"].encode() if "opaque" in header_dict else None
qop = header_dict["qop"].encode() if "qop" in header_dict else None
return _DigestAuthChallenge(
realm=realm, nonce=nonce, algorithm=algorithm, opaque=opaque, qop=qop
)
except KeyError as exc:
message = "Malformed Digest WWW-Authenticate header"
raise ProtocolError(message, request=request) from exc
def _build_auth_header(
self, request: Request, challenge: _DigestAuthChallenge
) -> str:
hash_func = self._ALGORITHM_TO_HASH_FUNCTION[challenge.algorithm.upper()]
def digest(data: bytes) -> bytes:
return hash_func(data).hexdigest().encode()
A1 = b":".join((self._username, challenge.realm, self._password))
path = request.url.raw_path
A2 = b":".join((request.method.encode(), path))
# TODO: implement auth-int
HA2 = digest(A2)
nc_value = b"%08x" % self._nonce_count
cnonce = self._get_client_nonce(self._nonce_count, challenge.nonce)View on GitHub (pinned to b5addb64f0)
Solutions
- Inspect the raw 401 response's WWW-Authenticate header (send once with follow_redirects=False / without auth) to see which fields are missing.
- Do not use httpx.DigestAuth against that endpoint; switch to BasicAuth, a bearer token, or a custom Auth subclass.
- Fix or reconfigure the server/proxy so its Digest challenge includes at least realm and nonce.
Example fix
// before
client.get(url, auth=httpx.DigestAuth("u", "p"))
// after
client.get(url, headers={"Authorization": "Bearer <token>"}) Defensive patterns
Strategy: try-catch
Validate before calling
import httpx
# Probe the endpoint once to inspect the challenge before relying on DigestAuth
probe = client.get(url)
if probe.status_code == 401:
wa = probe.headers.get_list("www-authenticate")
has_digest = any(h.lower().startswith("digest ") for h in wa)
field_keys = set()
for h in wa:
if h.lower().startswith("digest "):
for part in h.split(None, 1)[1].split(","):
k = part.split("=", 1)[0].strip()
field_keys.add(k.lower())
digest_ok = has_digest and {"realm", "nonce"} <= field_keys
# if not digest_ok, do not attempt DigestAuth Type guard
def has_required_digest_fields(www_auth_values: list[str]) -> bool:
for h in www_auth_values:
if h.lower().startswith("digest "):
fields = {}
for part in h.split(None, 1)[1].split(","):
k, _, v = part.partition("=")
fields[k.strip().lower()] = v.strip().strip('"')
return "realm" in fields and "nonce" in fields
return False Try / catch
try:
resp = client.get(url, auth=httpx.DigestAuth(user, pw))
except httpx.ProtocolError as exc:
# Server returned a malformed Digest challenge
log.warning("Digest challenge malformed: %s", exc)
resp = client.get(url, headers={"Authorization": "Bearer <token>"}) Prevention
- Inspect the 401 WWW-Authenticate header before committing to DigestAuth.
- Prefer token-based auth for non-RFC-compliant servers.
- Always catch httpx.ProtocolError around authenticated requests.
When it happens
Trigger: Calling client.get(url, auth=httpx.DigestAuth(user, pw)) against a server whose 401 'WWW-Authenticate: Digest' header omits 'realm' or 'nonce' (e.g. a header like 'Digest opaque="x"' with no realm/nonce).
Common situations: Non-compliant or custom auth servers/proxies that issue a Digest challenge without all mandatory fields; servers returning a 'stale' indicator without realm; misconfigured reverse proxies.
Related errors
- Unexpected qop value "{qop!r}" in digest auth
- Digest auth-int support is not yet implemented
- Invalid "auth" argument: {auth!r}
- Invalid URL in location header: {exc}.
AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04).
Data as JSON: /data/errors/fdff53ce036779d1.json.
Report an issue: GitHub.