{"record":{"id":"d3f3bd3bc5a56115","repo":"aio-libs/aiohttp","slug":"attempt-to-decode-json-with-unexpected-mimetype","errorCode":null,"errorMessage":"Attempt to decode JSON with unexpected mimetype: %s","messagePattern":"Attempt to decode JSON with unexpected mimetype: (.+?)","errorType":"exception","errorClass":"ContentTypeError","httpStatus":null,"severity":"error","filePath":"aiohttp/client_reqrep.py","lineNumber":757,"sourceCode":"\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return self._body.decode(encoding, errors=errors)  # type: ignore[union-attr]\n\n    async def json(\n        self,\n        *,\n        encoding: str | None = None,\n        loads: JSONDecoder = DEFAULT_JSON_DECODER,\n        content_type: str | None = \"application/json\",\n    ) -> Any:\n        \"\"\"Read and decodes JSON response.\"\"\"\n        await self.read()\n\n        if content_type:\n            if not is_expected_content_type(self.content_type, content_type):\n                raise ContentTypeError(\n                    self.request_info,\n                    self.history,\n                    status=self.status,\n                    message=(\n                        \"Attempt to decode JSON with \"\n                        \"unexpected mimetype: %s\" % self.content_type\n                    ),\n                    headers=self.headers,\n                )\n\n        if encoding is None:\n            encoding = self.get_encoding()\n\n        return loads(self._body.decode(encoding))  # type: ignore[union-attr]\n\n    async def __aenter__(self) -> \"ClientResponse\":\n        self._in_context = True\n        return self","sourceCodeStart":739,"sourceCodeEnd":775,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/client_reqrep.py#L739-L775","documentation":"Raised as ContentTypeError (a subclass of ClientResponseError) from ClientResponse.json() when the response's Content-Type does not match the expected type. By default content_type='application/json' and the check is lenient (is_expected_content_type allows charset/suffix variants), but a clearly non-JSON mimetype (e.g. text/html) triggers the error. Pass content_type=None to disable the guard.","triggerScenarios":"Calling await resp.json() on a response whose Content-Type is not JSON-compatible (e.g. 'text/html' from an error page, 'text/plain', 'application/xml'). The is_expected_content_type check fails and ContentTypeError is raised after the body is read.","commonSituations":"API returning an HTML error page (500/502) instead of JSON; server mis-labeling JSON as text/plain; proxy/gateway injecting an HTML error; endpoint that returns JSON but with a wrong Content-Type header.","solutions":["Inspect resp.status and resp.content_type before calling .json(); handle non-2xx explicitly.","If you know the body is JSON regardless, disable the check: await resp.json(content_type=None).","Fix the server to send 'Content-Type: application/json'.","Catch ContentTypeError and fall back to manual parsing or logging."],"exampleFix":"# before\nasync with session.get(url) as resp:\n    data = await resp.json()  # ContentTypeError if server sends text/html\n\n# after — accept any content type\ndata = await resp.json(content_type=None)\n# or guard explicitly:\nif resp.status != 200:\n    text = await resp.text()\n    raise RuntimeError(f'server error: {text}')\ndata = await resp.json(content_type=None)","handlingStrategy":"try-catch","validationCode":"async def safe_json(resp):\n    if not resp.content_type.startswith('application/json'):\n        # disable the strict check or handle manually\n        return await resp.json(content_type=None)\n    return await resp.json()","typeGuard":"def looks_like_json(resp) -> bool:\n    ct = resp.content_type.lower()\n    return ct.startswith('application/json') or ct.endswith('+json')","tryCatchPattern":"from aiohttp import ContentTypeError\n\ntry:\n    data = await resp.json()\nexcept ContentTypeError:\n    text = await resp.text()\n    log.warning('Non-JSON response (status=%s, ct=%s): %s', resp.status, resp.content_type, text[:200])\n    # optionally parse manually if you know it's JSON despite the header\n    import json\n    data = json.loads(text)","preventionTips":["Check resp.status and resp.content_type before calling .json().","Pass content_type=None when the server is known to mislabel JSON.","Always handle non-2xx responses as text first, then decide on parsing."],"tags":["client","response","json","content-type","http"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}