aio-libs/aiohttp · error · TypeError

content_type must be an instance of str. Got: %s

Error message

content_type must be an instance of str. Got: %s

What it means

Raised by FormData.add_field when content_type is not None but not a str. Content-Type becomes a literal HTTP header value, so non-string types are rejected up front with a TypeError. Setting content_type also forces multipart mode (self._is_multipart = True) after the type check passes.

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 c0ef574e29)

Solutions

  1. Ensure content_type is a plain str or None.
  2. Guard: content_type=ct if isinstance(ct, str) else None.
  3. Default to 'application/octet-stream' when mimetypes returns None.

Example fix

// before
ct = mimetypes.guess_type(name)[0]
form.add_field('f', data, content_type=ct)
// after
ct = mimetypes.guess_type(name)[0] or 'application/octet-stream'
form.add_field('f', data, content_type=ct)
Defensive patterns

Strategy: type-guard

Validate before calling

def safe_content_type(ct):
    if ct is None:
        return None
    if not isinstance(ct, str):
        raise TypeError('content_type must be str')
    return ct

Type guard

def is_str_or_none(ct) -> bool:
    return ct is None or isinstance(ct, str)

Prevention

When it happens

Trigger: form.add_field('img', data, content_type=None) is fine, but content_type=123 or a mimetypes.guess_type tuple element used directly fails. Passing a content-type with non-ASCII bytes object also fails.

Common situations: Using mimetypes.guess_type(file)[0] which can return None and then defaulting incorrectly; passing a tuple instead of the string; dynamically building content_type from a lookup dict that returns int codes.

Related errors


AI-assisted analysis of aio-libs/aiohttp@c0ef574e29 (2026-08-04). Data as JSON: /data/errors/ffd4d050ec029d61.json. Report an issue: GitHub.