{"record":{"id":"2f7e854212ecab2a","repo":"aio-libs/aiohttp","slug":"bad-line-ending-expected-crlf","errorCode":null,"errorMessage":"Bad line ending, expected CRLF","messagePattern":"Bad line ending, expected CRLF","errorType":"exception","errorClass":"BadHttpMessage","httpStatus":400,"severity":"error","filePath":"aiohttp/http_parser.py","lineNumber":523,"sourceCode":"                        elif upgraded:\n                            # No body to read, so the connection switches to\n                            # the upgraded protocol immediately.\n                            self._upgraded = True\n                            payload = EMPTY_PAYLOAD\n                        else:\n                            payload = EMPTY_PAYLOAD\n\n                        messages.append((msg, payload))\n                        if self._max_msg_queue_size:\n                            self._msg_in_flight += 1\n                        should_close = msg.should_close\n                else:\n                    self._tail = data[start_pos:]\n                    # A bare LF here means CRLF was required:\n                    # reject instead of buffering, else a following request's\n                    # bytes get appended to this line and leak in the error.\n                    if b\"\\n\" in self._tail:\n                        raise BadHttpMessage(\"Bad line ending, expected CRLF\")\n                    if len(self._tail) > self.max_line_size:\n                        raise LineTooLong(self._tail[:100] + b\"...\", self.max_line_size)\n                    data = EMPTY\n                    break\n\n            # no parser, just store\n            elif self._payload_parser is None and self._upgraded:\n                assert not self._lines\n                break\n\n            # feed payload\n            else:\n                assert not self._lines\n                assert self._payload_parser is not None\n                try:\n                    payload_state, data = self._payload_parser.feed_data(\n                        data[start_pos:], SEP\n                    )","sourceCodeStart":505,"sourceCodeEnd":541,"githubUrl":"https://github.com/aio-libs/aiohttp/blob/d041d4d0fd48c3f0832084d33be16cf1c4835f85/aiohttp/http_parser.py#L505-L541","documentation":"Raised in strict (non-lax) mode when a bare LF is found in the buffered tail without a preceding CR. Strict parsing requires CRLF line terminators per RFC 9112; lax mode accepts bare LF. The parser rejects rather than buffering so that bytes from the following request do not leak into the error message.","triggerScenarios":"A client sends HTTP/1.1 request lines or headers terminated with \\n instead of \\r\\n to the strict request parser (server-side default).","commonSituations":"Hand-written HTTP via socket.send(b'GET / HTTP/1.1\\n'), netcat/telnet-style clients, certain buggy embedded clients, naive test scripts using '\\n'.","solutions":["Terminate every HTTP/1.1 line with CRLF (\\r\\n).","Use a real HTTP client library instead of raw sockets.","Do not switch the server parser to lax solely to tolerate bare LF."],"exampleFix":"# before\nsock.send(b'GET / HTTP/1.1\\nHost: a\\n\\n')\n\n# after\nsock.send(b'GET / HTTP/1.1\\r\\nHost: a\\r\\n\\r\\n')","handlingStrategy":"validation","validationCode":"def uses_crlf(raw: bytes) -> bool:\n    # no bare LF not preceded by CR\n    return b'\\r\\n' in raw and b'\\n' not in raw.replace(b'\\r\\n', b'')","typeGuard":"def terminated_with_crlf(raw: bytes) -> bool:\n    stripped = raw.replace(b'\\r\\n', b'')\n    return b'\\n' not in stripped and b'\\r' not in stripped","tryCatchPattern":"from aiohttp.http_exceptions import BadHttpMessage\ntry:\n    ...parse...\nexcept BadHttpMessage as e:\n    if 'expected CRLF' in str(e):\n        transport.close()","preventionTips":["Always emit \\r\\n in hand-written HTTP.","When testing with netcat, configure it to send CRLF (e.g. 'set crlf' in telnet)."],"tags":["http","parser","protocol","validation"],"backgroundTag":null,"analyzedSha":"d041d4d0fd48c3f0832084d33be16cf1c4835f85","analyzedAt":"2026-08-11T20:44:15.550Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}