aio-libs/aiohttp · error · ClientError

Malformed Digest auth challenge: Missing 'realm' parameter

Error message

Malformed Digest auth challenge: Missing 'realm' parameter

What it means

Raised by aiohttp's Digest auth middleware when a server's WWW-Authenticate Digest challenge omits the required 'realm' parameter. RFC 7616 mandates realm in every Digest challenge, so aiohttp refuses to build an authorization header without it and raises ClientError. The error surfaces during the middleware's response-challenge handling, after a 401 is received and the cached challenge dict is inspected in _encode().

Solutions

  1. Inspect the raw response headers (use a plain aiohttp request without the middleware) to confirm the server's WWW-Authenticate value is missing realm.
  2. Fix the server / proxy configuration so the Digest challenge includes realm (e.g. 'WWW-Authenticate: Digest realm="myhost", nonce=...').
  3. If you cannot change the server, drop DigestAuthMiddleware for that endpoint and handle auth manually or use Basic auth.
  4. Verify no intermediary (CDN, gateway) is rewriting or stripping the WWW-Authenticate header.

Example fix

# before
app = DigestAuthMiddleware(login='u', password='p')
await session.get('https://bad-server/digest')  # server omits realm

# after — confirm the challenge first
resp = await session.get('https://bad-server/digest')
print(resp.headers.get('WWW-Authenticate'))
# then fix the server to send: Digest realm="myhost", nonce="..."
Defensive patterns

Strategy: try-catch

Validate before calling

# Before using digest middleware, probe the challenge
async def get_challenge(session, url):
    resp = await session.get(url)
    wa = resp.headers.get('WWW-Authenticate', '')
    await resp.release()
    return wa

# check the header contains realm=
challenge = await get_challenge(session, url)
if 'realm=' not in challenge.lower():
    raise RuntimeError(f'Server Digest challenge missing realm: {challenge}')

Type guard

def has_realm(www_authenticate_header: str) -> bool:
    return 'realm=' in www_authenticate_header.lower()

Try / catch

from aiohttp import ClientError

try:
    async with session.get(url, headers=mw_headers) as resp:
        ...
except ClientError as e:
    if 'realm' in str(e):
        log.error('Digest challenge malformed (no realm); check server WWW-Authenticate')
        # fall back to non-digest auth or surface to user
    raise

Prevention

When it happens

Trigger: Configuring a DigestAuthMiddleware (or TraceConfig-based digest client) and sending a request to a server that returns 'WWW-Authenticate: Digest' with no realm parameter (e.g. 'WWW-Authenticate: Digest nonce=abc'). The error fires when aiohttp attempts to compute the Authorization header from that incomplete challenge.

Common situations: Misconfigured or non-compliant reverse proxy / origin server omitting realm; home-grown test servers returning a hand-crafted Digest header; proxies that strip or rewrite WWW-Authenticate; connecting through an intermediary that mangles the challenge.

Understand the failure class

Related errors


AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11). Data as JSON: /api/errors/d37509dd59133fc5. Report an issue: GitHub.

Appendix: source

Thrown at aiohttp/client_middleware_digest_auth.py:243

        """
        Build digest authorization header for the current challenge.

        Args:
            method: The HTTP method (GET, POST, etc.)
            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"
            )

View on GitHub (pinned to d041d4d0fd)