{"record":{"id":"ffd4d050ec029d61","repo":"aio-libs/aiohttp","slug":"content-type-must-be-an-instance-of-str-got-s","errorCode":null,"errorMessage":"content_type must be an instance of str. Got: %s","messagePattern":"content_type must be an instance of str\\. Got: (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"aiohttp/formdata.py","lineNumber":74,"sourceCode":"    ) -> None:\n        if isinstance(value, (io.IOBase, bytes, bytearray, memoryview)):\n            self._is_multipart = True\n\n        _safe_header(name)\n        type_options: MultiDict[str] = MultiDict({\"name\": name})\n        if filename is not None and not isinstance(filename, str):\n            raise TypeError(\"filename must be an instance of str. Got: %s\" % filename)\n        if filename is None and isinstance(value, io.IOBase):\n            filename = guess_filename(value, name)\n        if filename is not None:\n            _safe_header(filename)\n            type_options[\"filename\"] = filename\n            self._is_multipart = True\n\n        headers = {}\n        if content_type is not None:\n            if not isinstance(content_type, str):\n                raise TypeError(\n                    \"content_type must be an instance of str. Got: %s\" % content_type\n                )\n            _safe_header(content_type)\n            headers[hdrs.CONTENT_TYPE] = content_type\n            self._is_multipart = True\n\n        self._fields.append((type_options, headers, value))\n\n    def add_fields(self, *fields: Any) -> None:\n        to_add: deque[Any] = deque(fields)\n\n        while to_add:\n            rec = to_add.popleft()\n\n            if isinstance(rec, io.IOBase):\n                k = guess_filename(rec, \"unknown\")\n                self.add_field(k, rec)  # type: ignore[arg-type]\n","sourceCodeStart":56,"sourceCodeEnd":92,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/formdata.py#L56-L92","documentation":"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.","triggerScenarios":"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.","commonSituations":"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).","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"],"exampleFix":"# before\nfrom enum import Enum\nclass CT(Enum):\n    PNG = 'image/png'\nform.add_field('img', data, content_type=CT.PNG)\n\n# after\nform.add_field('img', data, content_type=CT.PNG.value)","handlingStrategy":"type-guard","validationCode":"def coerce_content_type(ct):\n    if ct is None:\n        return None\n    if isinstance(ct, bytes):\n        return ct.decode('utf-8')\n    if not isinstance(ct, str):\n        return str(ct)\n    return ct\n\nform.add_field('file', data, content_type=coerce_content_type(ct))","typeGuard":"def is_valid_content_type(ct) -> bool:\n    return ct is None or isinstance(ct, str)","tryCatchPattern":null,"preventionTips":["Always use plain string literals for content types","If content types come from an enum or config, call str() or .value before passing"],"tags":["form-data","type-error","multipart","validation"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}