{"id":"055b980742b114c8","repo":"aio-libs/aiohttp","slug":"data-cannot-be-decoded-with-s-encoding","errorCode":null,"errorMessage":"data cannot be decoded with %s encoding","messagePattern":"data cannot be decoded with (.+?) encoding","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/multipart.py","lineNumber":517,"sourceCode":"        data = await self.read(decode=True)\n        if not data:\n            return None\n        encoding = encoding or self.get_charset(default=\"utf-8\")\n        return cast(dict[str, Any], json.loads(data.decode(encoding)))\n\n    async def form(self, *, encoding: str | None = None) -> list[tuple[str, str]]:\n        \"\"\"Like read(), but assumes that body parts contain form urlencoded data.\"\"\"\n        data = await self.read(decode=True)\n        if not data:\n            return []\n        if encoding is not None:\n            real_encoding = encoding\n        else:\n            real_encoding = self.get_charset(default=\"utf-8\")\n        try:\n            decoded_data = data.rstrip().decode(real_encoding)\n        except UnicodeDecodeError:\n            raise ValueError(\"data cannot be decoded with %s encoding\" % real_encoding)\n\n        return parse_qsl(\n            decoded_data,\n            keep_blank_values=True,\n            encoding=real_encoding,\n        )\n\n    def at_eof(self) -> bool:\n        \"\"\"Returns True if the boundary was reached or False otherwise.\"\"\"\n        return self._at_eof\n\n    def _apply_content_transfer_decoding(self, data: bytes) -> bytes:\n        \"\"\"Apply Content-Transfer-Encoding decoding if header is present.\"\"\"\n        if CONTENT_TRANSFER_ENCODING in self.headers:\n            return self._decode_content_transfer(data)\n        return data\n\n    def _needs_content_decoding(self) -> bool:","sourceCodeStart":499,"sourceCodeEnd":535,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/multipart.py#L499-L535","documentation":"Raised by BodyPartReader.form() when the body part's bytes cannot be decoded using the charset resolved from the Content-Type header (or the encoding argument) before being parsed as application/x-www-form-urlencoded data. The underlying UnicodeDecodeError from bytes.decode() is converted into a ValueError so callers get a clear, single failure point. This guards the parse_qsl step, which would otherwise produce garbage or crash on mojibake.","triggerScenarios":"Calling `await part.form()` (or `await part.form(encoding='latin-1')`) on a BodyPartReader whose declared charset does not match the actual byte content. Typically happens when the upstream sender mislabels the charset (e.g. declares utf-8 but sends latin-1 / cp1252 bytes), or when binary data is accidentally routed through a text form part.","commonSituations":"Browser/upstream sending form fields with a wrong Content-Type charset; legacy servers emitting cp1252 labeled as utf-8; proxied requests where an intermediary re-encodes the body; debugging against curl with `--data-binary` that injects raw bytes.","solutions":["Pass the correct charset explicitly: `await part.form(encoding='cp1252')` matching what the sender actually used.","Fix the sender to advertise the correct charset in the part's Content-Type header.","If the part is genuinely binary, do not use form() — read raw bytes via `await part.read(decode=True)` and handle them directly.","Wrap the call in try/except ValueError and surface a 400 to the client rather than crashing the handler."],"exampleFix":"// before\nfields = await part.form()\n// after\ntry:\n    fields = await part.form(encoding='cp1252')\nexcept ValueError:\n    fields = await part.read(decode=True)  # fall back to raw bytes","handlingStrategy":"try-catch","validationCode":"declared = part.get_charset(default='utf-8')\nsample = await part.read_chunk(256)\ntry:\n    sample.decode(declared)\n    valid = True\nexcept UnicodeDecodeError:\n    valid = False\n# rewind not possible on a stream — prefer passing explicit encoding to form()","typeGuard":"def has_valid_charset(part) -> bool:\n    enc = part.get_charset(default='utf-8')\n    try:\n        codecs.lookup(enc)\n        return True\n    except LookupError:\n        return False","tryCatchPattern":"try:\n    fields = await part.form(encoding='utf-8')\nexcept ValueError as e:\n    # charset mismatch — try fallback or reject\n    fields = []","preventionTips":["Always pass the explicit encoding to form() when the upstream charset is known but unreliable.","Validate the Content-Type charset header before parsing form parts.","Log the declared charset on failure to diagnose sender mismatches quickly."],"tags":["multipart","encoding","form-data","charset"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}