fastapi: Response content longer than Content-Length error for DELETE and NoContent

First Check

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn’t find it.
  • I searched the FastAPI documentation, with the integrated search.
  • I already searched in Google “How to X in FastAPI” and didn’t find any information.
  • I already read and followed all the tutorial in the docs and didn’t find an answer.
  • I already checked if it is not related to FastAPI but to Pydantic.
  • I already checked if it is not related to FastAPI but to Swagger UI.
  • I already checked if it is not related to FastAPI but to ReDoc.

Commit to Help

  • I commit to help with one of those options 👆

Example Code

from fastapi import FastAPI, status

app = FastAPI()


@app.delete("/", status_code=status.HTTP_204_NO_CONTENT)
def read_root():
    return None

Description

Upon requesting above code I got expected response but my logs shows that there is an error in uvicorn. The problem exists for DELETE method and NoContent response status code (for HEAD there is no such problem)

ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/uvicorn/protocols/http/httptools_impl.py", line 372, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
    return await self.app(scope, receive, send)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/fastapi/applications.py", line 269, in __call__
    await super().__call__(scope, receive, send)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 93, in __call__
    raise exc
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 82, in __call__
    await self.app(scope, receive, sender)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
    raise e
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/routing.py", line 670, in __call__
    await route.handle(scope, receive, send)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/routing.py", line 266, in handle
    await self.app(scope, receive, send)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/routing.py", line 68, in app
    await response(scope, receive, send)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/responses.py", line 162, in __call__
    await send({"type": "http.response.body", "body": self.body})
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/exceptions.py", line 79, in sender
    await send(message)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/starlette/middleware/errors.py", line 159, in _send
    await send(message)
  File "/home/user/PycharmProjects/sample_app/venv/lib/python3.9/site-packages/uvicorn/protocols/http/httptools_impl.py", line 501, in send
    raise RuntimeError("Response content longer than Content-Length")
RuntimeError: Response content longer than Content-Length```

### Operating System

Linux

### Operating System Details

_No response_

### FastAPI Version

0.78.0

### Python Version

3.10

### Additional Context

_No response_

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 1
  • Comments: 17 (6 by maintainers)

Commits related to this issue

Most upvoted comments

This works as well:

from fastapi import FastAPI, status, Response

app = FastAPI()


@app.delete("/", status_code=status.HTTP_204_NO_CONTENT, response_class=Response)
def read_root():
    return None

Does FastAPI not have any regression tests? How is it that this wasn’t picked up when the starlette version was bumped?

OK, so I looked into this a bit and the issue looks to be related to a recent change in Starlette (see this discussion for a repro without FastAPI in the mix). It may be that Starlette used to suppress the content return on a JSONResponse if the response code was 204, but it no longer does.

I notice that, historically, there are a number of times when people have proposed adding code to FastAPI’s router to set the response class explicitly (to Response rather than JSONResponse) in case the response code is one of the STATUS_CODES_WITH_NO_BODY. Given the amount of back-and-forth in the Starlette code around this, perhaps @tiangolo it’s worth FastAPI making this change? If you agree I’m happy to take a shot at it. If not, how about explicit documentation in FastAPI that if you plan to return no content you need to specify the response class.

This seems to be an issue for me still on fastapi 0.82.0. It can be reproduced like this:

from fastapi import FastAPI, HTTPException
import uvicorn

app = FastAPI()


@app.get("/")
async def root():
    raise HTTPException(204)

if __name__ == '__main__':
    uvicorn.run(app, debug=True)

Visiting the page results in:

RuntimeError: Response content longer than Content-Length

It’s worth noting that it’s the 3rd issue related to this topic created this week, so the current state of things is a loss of time for many users

I ran into this issue when using a custom response class that was returning a string with unicode characters instead of returning bytes. For example to reproduce:

class CustomResponse(Response):
    media_type = "application/json"

    def render(self, content: Any) -> bytes:
         return '{"password":"•••••••","connect_timeout":10,"created_at":"2023-01-06T05:39:34.285672"}'

This appears to be fixed somewhat in 0.79.0, as now if the status code is one that doesn’t allow content, it will explicitly set the response body to b"", though in cases where you’re using another status code but want it to return no content, you still have to explicitly set the response_class to Response.