{"id":"f8f21d513ceece10","repo":"aio-libs/aiohttp","slug":"invalid-content-length-header-content-length-hdr","errorCode":null,"errorMessage":"Invalid Content-Length header: {content_length_hdr!r}","messagePattern":"Invalid Content-Length header: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"aiohttp/client_reqrep.py","lineNumber":864,"sourceCode":"            self.headers[hdrs.AUTHORIZATION] = encode_basic_auth(\n                url.user or \"\", url.password or \"\"\n            )\n\n    def _reset_writer(self, _: object = None) -> None:\n        self._writer_task = None\n\n    def _get_content_length(self) -> int | None:\n        \"\"\"Extract and validate Content-Length header value.\n\n        Returns parsed Content-Length value or None if not set.\n        Raises ValueError if header exists but cannot be parsed as an integer.\n        \"\"\"\n        if hdrs.CONTENT_LENGTH not in self.headers:\n            return None\n\n        content_length_hdr = self.headers[hdrs.CONTENT_LENGTH]\n        if not _DIGITS_RE.fullmatch(content_length_hdr):\n            raise ValueError(f\"Invalid Content-Length header: {content_length_hdr!r}\")\n        return int(content_length_hdr)\n\n    @property\n    def _writer(self) -> asyncio.Task[None] | None:\n        return self._writer_task\n\n    @_writer.setter\n    def _writer(self, writer: asyncio.Task[None]) -> None:\n        if self._writer_task is not None:\n            self._writer_task.remove_done_callback(self._reset_writer)\n        self._writer_task = writer\n        writer.add_done_callback(self._reset_writer)\n\n    def is_ssl(self) -> bool:\n        return self.url.scheme in _SSL_SCHEMES\n\n    @property\n    def ssl(self) -> \"SSLContext | bool | Fingerprint\":","sourceCodeStart":846,"sourceCodeEnd":882,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/c0ef574e29109210e96e652771ae4e7b88615fa4/aiohttp/client_reqrep.py#L846-L882","documentation":"Raised as ValueError in ClientRequest._get_content_length() when a Content-Length header is present but its value is not all ASCII digits (fails _DIGITS_RE.fullmatch). HTTP forbids non-digit Content-Length values; this guard catches malformed or tampered header values before the request is sent.","triggerScenarios":"Fires at line 862-864 when self.headers[CONTENT_LENGTH] is something like '10 ', '0x10', '1, 1' (comma from proxy chains), or ''. Most often triggered by manually set headers or headers injected by middleware.","commonSituations":"Manually setting Content-Length to a non-numeric value; duplicate Content-Length headers collapsing to '1, 1'; trailing whitespace from string concatenation; copying headers from another response verbatim.","solutions":["Let aiohttp compute Content-Length automatically from the body instead of setting it manually.","If you must set it, ensure the value is a clean decimal integer string.","Strip whitespace and remove duplicate headers before assignment.","When forwarding headers, collapse duplicates instead of concatenating with commas."],"exampleFix":"# before\nheaders['Content-Length'] = str(len(body)) + ' '   # trailing space\nheaders['Content-Length'] = '0x10'\n# after\n# (preferred) omit Content-Length; aiohttp sets it from the body\n# or\nheaders['Content-Length'] = str(int(len(body)))","handlingStrategy":"validation","validationCode":"import re\nif 'Content-Length' in headers:\n    val = headers['Content-Length']\n    assert re.fullmatch(r'\\d+', val), f'bad Content-Length {val!r}'","typeGuard":"def is_valid_content_length(value: str) -> bool:\n    import re\n    return bool(re.fullmatch(r'\\d+', str(value)))","tryCatchPattern":null,"preventionTips":["Let aiohttp compute Content-Length from the body whenever possible.","Never concatenate duplicate Content-Length headers.","Validate header values copied from upstream responses."],"tags":["http","headers","validation","request-smuggling"],"analyzedSha":"c0ef574e29109210e96e652771ae4e7b88615fa4","analyzedAt":"2026-08-04T19:51:05.467Z","schemaVersion":2}