aio-libs/aiohttp · error · ContentTypeError
Attempt to decode JSON with unexpected mimetype
Error message
Attempt to decode JSON with unexpected mimetype: %s
What it means
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.
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.
Example fix
# before
async with session.get(url) as resp:
data = await resp.json() # ContentTypeError if server sends text/html
# after — accept any content type
data = await resp.json(content_type=None)
# or guard explicitly:
if resp.status != 200:
text = await resp.text()
raise RuntimeError(f'server error: {text}')
data = await resp.json(content_type=None) Defensive patterns
Strategy: try-catch
Validate before calling
async def safe_json(resp):
if not resp.content_type.startswith('application/json'):
# disable the strict check or handle manually
return await resp.json(content_type=None)
return await resp.json() Type guard
def looks_like_json(resp) -> bool:
ct = resp.content_type.lower()
return ct.startswith('application/json') or ct.endswith('+json') Try / catch
from aiohttp import ContentTypeError
try:
data = await resp.json()
except ContentTypeError:
text = await resp.text()
log.warning('Non-JSON response (status=%s, ct=%s): %s', resp.status, resp.content_type, text[:200])
# optionally parse manually if you know it's JSON despite the header
import json
data = json.loads(text) Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- aiohttp only supports http(s) proxies
- Cannot compute fallback encoding of a not yet read body
- Connection closed
- 1002
- Bad HTTP method in status line
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/d3f3bd3bc5a56115.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/client_reqrep.py:757
if encoding is None:
encoding = self.get_encoding()
return self._body.decode(encoding, errors=errors) # type: ignore[union-attr]
async def json(
self,
*,
encoding: str | None = None,
loads: JSONDecoder = DEFAULT_JSON_DECODER,
content_type: str | None = "application/json",
) -> Any:
"""Read and decodes JSON response."""
await self.read()
if content_type:
if not is_expected_content_type(self.content_type, content_type):
raise ContentTypeError(
self.request_info,
self.history,
status=self.status,
message=(
"Attempt to decode JSON with "
"unexpected mimetype: %s" % self.content_type
),
headers=self.headers,
)
if encoding is None:
encoding = self.get_encoding()
return loads(self._body.decode(encoding)) # type: ignore[union-attr]
async def __aenter__(self) -> "ClientResponse":
self._in_context = True
return selfView on GitHub (pinned to d041d4d0fd)