aio-libs/aiohttp · error · ValueError
passing both Content-Type header and content_type or…
Error message
passing both Content-Type header and content_type or charset params is forbidden
What it means
Raised by the Response constructor in the `text is not None` branch when the caller supplies both a Content-Type header (in `headers=`) AND a `content_type=` or `charset=` kwarg. The two channels for setting Content-Type conflict; aiohttp refuses to silently pick one and produce an inconsistent header.
Solutions
- Choose ONE channel: either set Content-Type via `headers=` OR via `content_type=`/`charset=` kwargs, never both.
- If middleware sets defaults, have it omit Content-Type when the handler will pass content_type.
- Strip Content-Type from the headers dict before constructing the Response if you intend to use the kwargs.
- Build the headers dict without Content-Type and let the kwargs drive it.
Example fix
// before
return Response(
text=body,
headers={'Content-Type': 'text/html'},
content_type='text/html',
)
// after
return Response(text=body, content_type='text/html') Defensive patterns
Strategy: validation
Validate before calling
def build_response(text=None, body=None, headers=None, content_type=None, charset=None):
h = CIMultiDict(headers or {})
if 'Content-Type' in h:
assert content_type is None and charset is None, \
'do not pass content_type/charset with a Content-Type header'
return Response(text=text, body=body, headers=h, content_type=content_type, charset=charset) Type guard
from multidict import CIMultiDict
def headers_have_content_type(headers) -> bool:
return any(k.lower() == 'content-type' for k in (headers or {}).keys()) Prevention
- Choose one channel for Content-Type: headers= OR content_type=/charset=.
- Strip Content-Type from base headers when handlers will set content_type.
- Middleware should not inject Content-Type if handlers set it.
- Document the convention in helper signatures.
When it happens
Trigger: Calling `Response(text='hi', headers={'Content-Type': 'text/html'}, content_type='text/plain')`, or passing charset alongside a headers dict that already carries Content-Type. Common when wrapping responses with default headers and then overriding per-call.
Common situations: Middleware that injects a default Content-Type header into all responses; then a handler also passes content_type. Helpers that merge a base headers dict with caller-supplied content_type. Copy-pasting a headers dict that contains Content-Type and then adding the kwarg 'just in case'.
Understand the failure class
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- charset must not be in content_type argument
- body and text are not allowed together
- Setting charset for application/octet-stream doesn't make…
- text argument must be str (%r)
- Cannot call write() after write_eof()
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/e8215467631e4dde.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_response.py:568
charset: str | None = None,
zlib_executor_size: int = MAX_SYNC_CHUNK_SIZE,
zlib_executor: Executor | None = None,
) -> None:
if body is not None and text is not None:
raise ValueError("body and text are not allowed together")
if headers is None:
real_headers: CIMultiDict[str] = CIMultiDict()
else:
real_headers = CIMultiDict(headers)
if content_type is not None and "charset" in content_type:
raise ValueError("charset must not be in content_type argument")
if text is not None:
if hdrs.CONTENT_TYPE in real_headers:
if content_type or charset:
raise ValueError(
"passing both Content-Type header and "
"content_type or charset params "
"is forbidden"
)
else:
# fast path for filling headers
if not isinstance(text, str):
raise TypeError("text argument must be str (%r)" % type(text))
if content_type is None:
content_type = "text/plain"
if charset is None:
charset = "utf-8"
real_headers[hdrs.CONTENT_TYPE] = content_type + "; charset=" + charset
body = text.encode(charset)
text = None
elif hdrs.CONTENT_TYPE in real_headers:
if content_type is not None or charset is not None:
raise ValueError(View on GitHub (pinned to d041d4d0fd)