encode/httpx · error · ProtocolError
Unexpected qop value "{qop!r}" in digest auth
Error message
Unexpected qop value "{qop!r}" in digest auth What it means
Raised as httpx.ProtocolError by DigestAuth._resolve_qop when the server's qop value is present but contains neither 'auth' nor 'auth-int'. The split list does not match any qop value httpx can negotiate.
Source
Thrown at httpx/_auth.py:340
if field not in NON_QUOTED_FIELDS
else NON_QUOTED_TEMPLATE
)
header_value += template.format(field, to_str(value))
return header_value
def _resolve_qop(self, qop: bytes | None, request: Request) -> bytes | None:
if qop is None:
return None
qops = re.split(b", ?", qop)
if b"auth" in qops:
return b"auth"
if qops == [b"auth-int"]:
raise NotImplementedError("Digest auth-int support is not yet implemented")
message = f'Unexpected qop value "{qop!r}" in digest auth'
raise ProtocolError(message, request=request)
class _DigestAuthChallenge(typing.NamedTuple):
realm: bytes
nonce: bytes
algorithm: str
opaque: bytes | None
qop: bytes | None
View on GitHub (pinned to b5addb64f0)
Solutions
- Capture the 401 WWW-Authenticate header to confirm the invalid qop value.
- Do not use httpx.DigestAuth against that endpoint; use a custom Auth or token.
- Fix the server's Digest configuration to advertise qop=auth.
Example fix
// before
client.get(url, auth=httpx.DigestAuth("u", "p"))
// after
def auth_flow(req):
req.headers["Authorization"] = "Bearer <token>"
yield req
client.get(url, auth=auth_flow) Defensive patterns
Strategy: try-catch
Validate before calling
import re
probe = client.get(url)
for h in (probe.headers.get_list("www-authenticate") if probe.status_code == 401 else []):
if h.lower().startswith("digest "):
m = re.search(r'qop="?([^"]+)"?', h)
if m:
qops = [q.strip() for q in m.group(1).split(",")]
negotiable = "auth" in qops or qops == ["auth-int"]
# if not negotiable, DigestAuth will raise ProtocolError Type guard
def digest_qop_negotiable(www_auth_values: list[str]) -> bool:
import re
for h in www_auth_values:
if h.lower().startswith("digest "):
m = re.search(r'qop="?([^"]+)"?', h)
if m:
qops = [q.strip() for q in m.group(1).split(",")]
return "auth" in qops or qops == ["auth-int"]
return True Try / catch
try:
resp = client.get(url, auth=httpx.DigestAuth(user, pw))
except httpx.ProtocolError as exc:
log.warning("Unrecognized digest qop: %s", exc)
resp = client.get(url, headers={"Authorization": "Bearer <token>"}) Prevention
- Validate the qop value advertised by the server before using DigestAuth.
- Fall back to a non-digest scheme when the server's qop is unrecognized.
- Catch httpx.ProtocolError to keep callers resilient.
When it happens
Trigger: A 401 Digest challenge with an unrecognized qop token, e.g. 'Digest ... qop="foobar"' or a malformed 'qop=""'. _resolve_qop finds no 'auth' and the list is not exactly ['auth-int'].
Common situations: Non-compliant servers with typos in qop; proprietary qop extensions; corrupted or truncated WWW-Authenticate headers.
Related errors
- Malformed Digest WWW-Authenticate header
- 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/9cfaecb29b655f34.json.
Report an issue: GitHub.