Install & Compatibility
Where this runs
tested against v0.16.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
muslpy 3.10–3.95 runs
installs and imports cleanly · install 0.0s · import 0.064s · 18MB
glibcpy 3.10–3.95 runs
installs and imports cleanly · install 1.7s · import 0.058s · 18MB
16MB installed
● package 16MB
Code
Verified usage
Verified import paths — ran on the pinned version, not inferred.
Connection
✓ import h11; conn = h11.Connection(our_role=h11.CLIENT)
All public symbols live directly on the h11 namespace. Do not import from h11._connection or other private submodules.
Request / Response / Data / EndOfMessage / InformationalResponse / ConnectionClosed
✓ import h11 # then use h11.Request, h11.Response, etc.
All event classes are top-level on h11. Importing from h11._events is private API and may break across patch releases.
LocalProtocolError / RemoteProtocolError / ProtocolError
✓ import h11 # catch h11.LocalProtocolError, h11.RemoteProtocolError
✗ from h11._util import LocalProtocolError
These are re-exported at the top-level h11 namespace; importing from h11._util is private and unsupported.
NEED_DATA / PAUSED / CLIENT / SERVER
✓ import h11 # use h11.NEED_DATA, h11.PAUSED, h11.CLIENT, h11.SERVER
Sentinel values are identity-compared with 'is', not '=='. Use type(event) dispatch or identity checks.
Minimal synchronous HTTP/1.1 client using a raw socket. Demonstrates Connection setup, sending a Request + EndOfMessage, and draining events in a loop.
import socket
import ssl
import h11
HOST = "httpbin.org"
PORT = 443
ctx = ssl.create_default_context()
sock = ctx.wrap_socket(
socket.create_connection((HOST, PORT)),
server_hostname=HOST,
)
conn = h11.Connection(our_role=h11.CLIENT)
# Send request
sock.sendall(conn.send(h11.Request(
method="GET",
target="/get",
headers=[("Host", HOST), ("Connection", "close")],
)))
sock.sendall(conn.send(h11.EndOfMessage()))
# Receive events
while True:
event = conn.next_event()
if event is h11.NEED_DATA:
data = sock.recv(4096)
conn.receive_data(data)
continue
if isinstance(event, h11.Response):
print("Status:", event.status_code)
elif isinstance(event, h11.Data):
print("Body chunk:", event.data[:80])
elif isinstance(event, (h11.EndOfMessage, h11.ConnectionClosed)):
break
sock.close()
Debug
Known issues
breakingProtocolError was split into LocalProtocolError and RemoteProtocolError in v0.10. Catching the old bare ProtocolError still works (it is now an abstract base class), but code that previously caught ProtocolError to distinguish client vs server errors must be updated.fixCatch h11.LocalProtocolError for errors you caused and h11.RemoteProtocolError for peer misbehavior. Both inherit from h11.ProtocolError if you need a catch-all.
affects: <0.10
breakingPython 2 support was dropped in v0.12; Python 3.6 support dropped in v0.14. h11 now requires Python >= 3.8 (as of v0.16).fixUpgrade to Python >= 3.8. Pin h11 <= 0.11.x only if you must keep Python 2 support.
affects: <0.12 (Py2), <0.14 (Py3.6)
breakingOutgoing header validation became strict in v0.10+: headers with illegal characters or leading/trailing whitespace in values now raise LocalProtocolError. Previously, whitespace was silently stripped.fixStrip whitespace from header values before passing them to h11 event constructors, or fix the upstream source producing invalid header values.
affects: <0.10
breakingv0.16.0 rejects previously-accepted malformed Transfer-Encoding: chunked bodies (CVE / GHSA-vqfr-h8mv-ghfj). Code or test suites that construct intentionally malformed chunked bodies will now get a RemoteProtocolError.fixUpgrade to h11 >= 0.16.0. If you are behind a reverse proxy, also ensure the proxy correctly validates chunked encoding to prevent request smuggling.
affects: <0.16.0
gotchaProtocol errors are unrecoverable. Once LocalProtocolError or RemoteProtocolError is raised, the Connection state becomes ERROR and all subsequent send()/next_event() calls also raise. You cannot reset or reuse the connection.fixClose the socket and create a brand-new h11.Connection for the next request. Do not attempt to call start_next_cycle() after an error.
affects: all
gotchastart_next_cycle() for keep-alive reuse raises LocalProtocolError('not in a reusable state') if called before both sides have reached DONE state. Forgetting to send or fully consume EndOfMessage is a common cause.fixVerify conn.our_state is h11.DONE and conn.their_state is h11.DONE before calling conn.start_next_cycle(). Always fully drain response events (including EndOfMessage) before reusing.
affects: all
gotchaResponse bodies may be delivered across multiple Data events because h11 buffers as little as possible. Assuming a single Data event contains the full body will silently truncate data.fixAccumulate all Data event payloads in a list/bytearray and concatenate only after receiving EndOfMessage.
affects: all
Errors
Common errors & fixes
AttributeError: module 'h11' has no attribute 'Event'
This error typically arises when a dependent library (like `httpx` or `gradio`) expects a specific `Event` object or attribute from `h11` that is either not exposed directly, or due to version incompatibility where `h11` or its dependents are outdated.
fixUpgrade `h11` to the latest version (`pip install --upgrade h11`) and also upgrade any libraries that depend on `h11` (e.g., `pip install --upgrade httpcore httpx`). If `Event` is being directly referenced, check the `h11` documentation for the correct event class (e.g., `h11.Request`, `h11.Response`) as a generic `h11.Event` is not a top-level class.
ModuleNotFoundError: No module named 'h11._util'
This error occurs when a Python package, often one that indirectly depends on `h11` (like the OpenAI Python client), attempts to import an internal submodule `h11._util` that cannot be found. This usually indicates a broken `h11` installation, conflicting dependencies, or an incompatibility after an upgrade of `h11` or a dependent library.
fixReinstall or upgrade `h11` (`pip install --upgrade h11`) and its direct dependencies (e.g., `pip install --upgrade httpcore httpx`). Using a virtual environment and ensuring all packages are installed fresh can help resolve dependency conflicts.
h11._util.LocalProtocolError: Too much data for declared Content-Length
`h11` raises this `LocalProtocolError` when your application, acting as either a client or server, sends a message body that is larger than the `Content-Length` header it declared, violating the HTTP/1.1 protocol.
fixEnsure that the `Content-Length` header in your HTTP request or response accurately reflects the actual byte length of the body being sent. If using chunked transfer encoding, do not set `Content-Length`.
h11._util.RemoteProtocolError: illegal request line
`h11` raises this `RemoteProtocolError` when it receives an HTTP request or response line from a remote peer that does not conform to the HTTP/1.1 specification (e.g., malformed syntax, incorrect method/target, or unsupported HTTP version).
fixThis error indicates a problem with the remote peer's HTTP message. If you control the peer, fix its HTTP implementation. If you are a server receiving this, it suggests a client is sending invalid HTTP. If you are a client, the server might be sending an invalid initial response. You may need to inspect the raw bytes received to diagnose the specific non-compliance.
Upgrade
Version history
0.16.0latest on PyPI · released Apr 24, 2025
Audit
Dependencies
No dependency data recorded yet.