{"record":{"id":"d37509dd59133fc5","repo":"aio-libs/aiohttp","slug":"malformed-digest-auth-challenge-missing-realm-p","errorCode":null,"errorMessage":"Malformed Digest auth challenge: Missing 'realm' parameter","messagePattern":"Malformed Digest auth challenge: Missing 'realm' parameter","errorType":"exception","errorClass":"ClientError","httpStatus":null,"severity":"error","filePath":"aiohttp/client_middleware_digest_auth.py","lineNumber":243,"sourceCode":"        \"\"\"\n        Build digest authorization header for the current challenge.\n\n        Args:\n            method: The HTTP method (GET, POST, etc.)\n            url: The request URL\n            body: The request body (used for qop=auth-int)\n\n        Returns:\n            A fully formatted Digest authorization header string\n\n        Raises:\n            ClientError: If the challenge is missing required parameters or\n                         contains unsupported values\n\n        \"\"\"\n        challenge = self._challenge\n        if \"realm\" not in challenge:\n            raise ClientError(\n                \"Malformed Digest auth challenge: Missing 'realm' parameter\"\n            )\n\n        if \"nonce\" not in challenge:\n            raise ClientError(\n                \"Malformed Digest auth challenge: Missing 'nonce' parameter\"\n            )\n\n        # Empty realm values are allowed per RFC 7616 (SHOULD, not MUST, contain host name)\n        realm = challenge[\"realm\"]\n        nonce = challenge[\"nonce\"]\n\n        # Empty nonce values are not allowed as they are security-critical for replay protection\n        if not nonce:\n            raise ClientError(\n                \"Security issue: Digest auth challenge contains empty 'nonce' value\"\n            )\n","sourceCodeStart":225,"sourceCodeEnd":261,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/client_middleware_digest_auth.py#L225-L261","documentation":"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().","triggerScenarios":"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.","commonSituations":"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.","solutions":["Inspect the raw response headers (use a plain aiohttp request without the middleware) to confirm the server's WWW-Authenticate value is missing realm.","Fix the server / proxy configuration so the Digest challenge includes realm (e.g. 'WWW-Authenticate: Digest realm=\"myhost\", nonce=...').","If you cannot change the server, drop DigestAuthMiddleware for that endpoint and handle auth manually or use Basic auth.","Verify no intermediary (CDN, gateway) is rewriting or stripping the WWW-Authenticate header."],"exampleFix":"# before\napp = DigestAuthMiddleware(login='u', password='p')\nawait session.get('https://bad-server/digest')  # server omits realm\n\n# after — confirm the challenge first\nresp = await session.get('https://bad-server/digest')\nprint(resp.headers.get('WWW-Authenticate'))\n# then fix the server to send: Digest realm=\"myhost\", nonce=\"...\"","handlingStrategy":"try-catch","validationCode":"# Before using digest middleware, probe the challenge\nasync def get_challenge(session, url):\n    resp = await session.get(url)\n    wa = resp.headers.get('WWW-Authenticate', '')\n    await resp.release()\n    return wa\n\n# check the header contains realm=\nchallenge = await get_challenge(session, url)\nif 'realm=' not in challenge.lower():\n    raise RuntimeError(f'Server Digest challenge missing realm: {challenge}')","typeGuard":"def has_realm(www_authenticate_header: str) -> bool:\n    return 'realm=' in www_authenticate_header.lower()","tryCatchPattern":"from aiohttp import ClientError\n\ntry:\n    async with session.get(url, headers=mw_headers) as resp:\n        ...\nexcept ClientError as e:\n    if 'realm' in str(e):\n        log.error('Digest challenge malformed (no realm); check server WWW-Authenticate')\n        # fall back to non-digest auth or surface to user\n    raise","preventionTips":["Always inspect the raw WWW-Authenticate header before relying on Digest middleware.","Maintain a server-configuration checklist that requires realm + nonce in Digest challenges.","In CI, run a smoke test against the real auth endpoint to catch regressions early."],"tags":["digest-auth","authentication","http","client-middleware"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}