aio-libs/aiohttp · error · RuntimeError
Setting charset for application/octet-stream doesn't make…
Error message
Setting charset for application/octet-stream doesn't make sense, setup content_type first
What it means
Raised by Response.charset setter when the current content_type is the default 'application/octet-stream'. aiohttp refuses to attach a charset to a binary mime type because the Content-Type header would become semantically meaningless (e.g. 'application/octet-stream; charset=utf-8'). You must first set a text-oriented content_type so the charset is meaningful.
Solutions
- Set `resp.content_type = 'text/html'` (or another text/* or application/json type) BEFORE assigning `resp.charset`.
- Construct the response with the content_type up front: `Response(text=body, content_type='text/html; charset=utf-8')` or pass `text=` which auto-sets both content_type and charset.
- If you genuinely have binary data, do not set a charset at all — leave the default octet-stream.
- Avoid string-concatenating '; charset=...' into content_type manually; use the dedicated charset property after a valid text content_type.
Example fix
// before resp = StreamResponse() resp.charset = 'utf-8' # RuntimeError // after resp = StreamResponse() resp.content_type = 'text/plain' resp.charset = 'utf-8'
Defensive patterns
Strategy: validation
Validate before calling
def set_charset(resp, charset):
if resp.content_type in ('application/octet-stream', None):
resp.content_type = 'text/plain' # or another text type
resp.charset = charset Type guard
def is_text_content_type(ctype: str) -> bool:
return ctype.startswith('text/') or ctype in (
'application/json', 'application/javascript',
'application/xml', 'application/xhtml+xml',
) Prevention
- Set content_type to a text/* or application/json type before assigning charset.
- Prefer Response(text=...) which sets content_type and charset together.
- Treat octet-stream bodies as bytes-only — never attach a charset.
- In helpers, normalize content_type first, then charset.
When it happens
Trigger: Calling `resp.charset = 'utf-8'` on a freshly constructed `Response()` (or StreamResponse) without having set `resp.content_type` to a text type first. Also triggered by `Response(content_type='application/octet-stream', charset='utf-8')` if the content_type setter leaves the default, or by mutating charset after resetting content_type back to the default.
Common situations: Building a generic Response and forgetting to pass `content_type='text/plain'` (or `text/html`, `application/json`) before assigning charset. Migrating code from explicitly-typed responses to a base StreamResponse and dropping the content_type line. The error is a deliberate guard because octet-stream bodies are bytes and charset is meaningless there.
Related errors
- charset must not be in content_type argument
- passing both Content-Type header and content_type or…
- body and text are not allowed together
- Cannot call write() after write_eof()
- Cannot call write() before prepare()
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/f9e9ce57e36556ba.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/web_response.py:240
# Just a placeholder for adding setter
return super().content_type
@content_type.setter
def content_type(self, value: str) -> None:
self.content_type # read header values if needed
self._content_type = str(value)
self._generate_content_type_header()
@property
def charset(self) -> str | None:
# Just a placeholder for adding setter
return super().charset
@charset.setter
def charset(self, value: str | None) -> None:
ctype = self.content_type # read header values if needed
if ctype == "application/octet-stream":
raise RuntimeError(
"Setting charset for application/octet-stream "
"doesn't make sense, setup content_type first"
)
assert self._content_dict is not None
if value is None:
self._content_dict.pop("charset", None)
else:
self._content_dict["charset"] = str(value).lower()
self._generate_content_type_header()
@property
def last_modified(self) -> datetime.datetime | None:
"""The value of Last-Modified HTTP header, or None.
This header is represented as a `datetime` object.
"""
return parse_http_date(self._headers.get(hdrs.LAST_MODIFIED))
View on GitHub (pinned to d041d4d0fd)