From 0d35d2ae4418a99a411f01342b2861d8ce6c7acf Mon Sep 17 00:00:00 2001 From: Benoit Chesneau Date: Sun, 3 May 2026 20:25:23 +0200 Subject: [PATCH] fix: warn once when ASGI app emits a body for a no-body response MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A framework bug — say, returning bytes from a HEAD or 204 handler — is now logged at WARNING level the first time it happens for a request so the misbehavior is visible without spamming on multi-chunk streams. --- gunicorn/asgi/protocol.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/gunicorn/asgi/protocol.py b/gunicorn/asgi/protocol.py index 19ec0477..fe9b610d 100644 --- a/gunicorn/asgi/protocol.py +++ b/gunicorn/asgi/protocol.py @@ -906,6 +906,7 @@ class ASGIProtocol(asyncio.Protocol): exc_to_raise = None use_chunked = False omits_body = False + omits_body_warned = False # Reset response buffer for write batching self._response_buffer = None @@ -921,6 +922,7 @@ class ASGIProtocol(asyncio.Protocol): async def send(message): nonlocal response_started, response_complete, exc_to_raise nonlocal response_status, response_headers, response_sent, use_chunked, omits_body + nonlocal omits_body_warned # If client disconnected, silently ignore send attempts # This allows apps to finish cleanup without errors @@ -994,10 +996,19 @@ class ASGIProtocol(asyncio.Protocol): more_body = message.get("more_body", False) # RFC 9110: HEAD/1xx/204/304 responses must not carry a body, - # even if the framework emits one. Drop body bytes silently; + # even if the framework emits one. Drop body bytes; # use_chunked has already been forced False above so no - # terminator will be written either. + # terminator will be written either. Warn once per request + # so framework bugs surface in logs without spamming on + # multi-chunk streams. if omits_body: + if body and not omits_body_warned: + self.log.warning( + "ASGI app sent body bytes on a no-body response " + "(method=%s status=%s); dropping per RFC 9110.", + request.method, response_status, + ) + omits_body_warned = True body = b"" if body: