aio-libs/aiohttp · error · TypeError
content_type must be an instance of str. Got
Error message
content_type must be an instance of str. Got: %s
What it means
FormData.add_field raises TypeError when content_type is not None and not a str instance. The content_type becomes the Content-Type header value for the multipart part, so it must be a plain string such as 'image/png'. This is a type validation guard.
Solutions
- Convert the content type to str: form.add_field('file', data, content_type=str(ct))
- If using an Enum, ensure it has a string value and pass .value
- Hard-code the content type string if it is a constant
Example fix
# before
from enum import Enum
class CT(Enum):
PNG = 'image/png'
form.add_field('img', data, content_type=CT.PNG)
# after
form.add_field('img', data, content_type=CT.PNG.value) Defensive patterns
Strategy: type-guard
Validate before calling
def coerce_content_type(ct):
if ct is None:
return None
if isinstance(ct, bytes):
return ct.decode('utf-8')
if not isinstance(ct, str):
return str(ct)
return ct
form.add_field('file', data, content_type=coerce_content_type(ct)) Type guard
def is_valid_content_type(ct) -> bool:
return ct is None or isinstance(ct, str) Prevention
- Always use plain string literals for content types
- If content types come from an enum or config, call str() or .value before passing
When it happens
Trigger: Passing a non-str object as content_type, such as an enum member, bytes, or a custom class with a __str__ method. Common when content types come from a configuration object or enum without conversion.
Common situations: Using an Enum for content types and passing the member directly; passing bytes from a header parsing result; accidentally passing a tuple of (type, subtype).
Related errors
- filename must be an instance of str. Got
- Only io.IOBase, multidict and (name, file) pairs allowed…
- Can not serialize value type: %r headers: %r value: %r
- Multipart field missing name.
- boundary missed for Content-Type
AI-assisted analysis of aio-libs/aiohttp@d041d4d0fd (2026-08-11).
Data as JSON: /api/errors/ffd4d050ec029d61.
Report an issue: GitHub.
Appendix: source
Thrown at aiohttp/formdata.py:74
) -> None:
if isinstance(value, (io.IOBase, bytes, bytearray, memoryview)):
self._is_multipart = True
_safe_header(name)
type_options: MultiDict[str] = MultiDict({"name": name})
if filename is not None and not isinstance(filename, str):
raise TypeError("filename must be an instance of str. Got: %s" % filename)
if filename is None and isinstance(value, io.IOBase):
filename = guess_filename(value, name)
if filename is not None:
_safe_header(filename)
type_options["filename"] = filename
self._is_multipart = True
headers = {}
if content_type is not None:
if not isinstance(content_type, str):
raise TypeError(
"content_type must be an instance of str. Got: %s" % content_type
)
_safe_header(content_type)
headers[hdrs.CONTENT_TYPE] = content_type
self._is_multipart = True
self._fields.append((type_options, headers, value))
def add_fields(self, *fields: Any) -> None:
to_add: deque[Any] = deque(fields)
while to_add:
rec = to_add.popleft()
if isinstance(rec, io.IOBase):
k = guess_filename(rec, "unknown")
self.add_field(k, rec) # type: ignore[arg-type]
View on GitHub (pinned to d041d4d0fd)