Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Response content longer than Content-Length error for DELETE and NoContent #4939

Closed
9 tasks done
myslak71 opened this issue May 23, 2022 · 17 comments
Closed
9 tasks done
Labels
answered bug Something isn't working reviewed

Comments

@myslak71
Copy link

myslak71 commented May 23, 2022

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_
@myslak71 myslak71 added the question Question or problem label May 23, 2022
@aboubacs
Copy link

This should help: #717 (comment)

@aboubacs
Copy link

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

@brotskydotcom
Copy link
Contributor

Thanks @aboubacs! I was having the same issue returning 204 as a POST response. @tiangolo This worked as recently as 0.75.2 without a special response class. IIRC, we had this same problem a long time ago before I contributed the special code for recognizing 204 as a no-content response. My opinion is that 204 as the declared status code should still work without the response_class annotation, and that this is a regression. If I have time I will look at contributing a fix.

@brotskydotcom
Copy link
Contributor

brotskydotcom commented May 24, 2022

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.

@aboubacs
Copy link

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

@leokhachatorians
Copy link

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

@FISHMANPET
Copy link

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.

@fboerman
Copy link

hi I get the same error when doing:

raise HTTPException(status_code=HTTP_304_NOT_MODIFIED)

I am running fastapi 0.79.0

@fboerman
Copy link

I think #4952 is a fix for this

@tiangolo
Copy link
Owner

tiangolo commented Sep 3, 2022

Thanks for the report here @myslak71! 🍰

And thanks for the discussion everyone. ☕

It seems this was handled in #5145, available since FastAPI 0.79.0.

If that solves it, you could close the issue. Thanks! 🍰

@tiangolo tiangolo added bug Something isn't working answered and removed question Question or problem labels Sep 3, 2022
@myslak71 myslak71 closed this as completed Sep 3, 2022
@gazpachoking
Copy link

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

@leokhachatorians
Copy link

Any updates on this?

@JarroVGIT
Copy link
Contributor

I cannot reproduce with the example of @gazpachoking . This is my cURL:

[jarro@MBP-van-Jarro ~ % curl localhost:8000 -v
*   Trying 127.0.0.1:8000...
* Connected to localhost (127.0.0.1) port 8000 (#0)
> GET / HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.79.1
> Accept: */*
> 
* Mark bundle as not supporting multiuse
< HTTP/1.1 204 No Content
< date: Sun, 18 Sep 2022 20:50:13 GMT
< server: uvicorn
< 
* Connection #0 to host localhost left intact

Which seems correct. My logs are clean as well:

INFO:     Started server process [28938]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     127.0.0.1:51631 - "GET / HTTP/1.1" 204 No Content

Running on Python 3.10.5, uvicorn 0.18.2, FastAPI 0.83:

Package            Version
------------------ ---------
anyio              3.6.1
attrs              22.1.0
black              22.6.0
certifi            2022.6.15
charset-normalizer 2.1.0
click              8.1.3
coverage           6.4.3
fastapi            0.83.0
h11                0.13.0
idna               3.3
iniconfig          1.1.1
mypy-extensions    0.4.3
packaging          21.3
pathspec           0.9.0
pip                22.0.4
platformdirs       2.5.2
pluggy             1.0.0
py                 1.11.0
pydantic           1.9.1
pyparsing          3.0.9
pytest             7.1.2
pytest-cov         3.0.0
python-multipart   0.0.5
requests           2.28.1
setuptools         58.1.0
six                1.16.0
sniffio            1.2.0
starlette          0.19.1
tomli              2.0.1
typing_extensions  4.3.0
urllib3            1.26.11
uvicorn            0.18.2

@brotskydotcom
Copy link
Contributor

FWIW, I don't believe the behavior reported by @gazpachoking, even if it's reproducible, is a bug. 204 is an "OK" status response with no body. All request error (400-series) and server-error (500-series) responses are expected to have a body which explains the problem, and the HTTPException response is designed for these responses. So producing an HTTPException response with no body feels like a logic bug that neither FastAPI nor Starlette really needs to guard against.

@gazpachoking
Copy link

This was discussed and handled in #4949 and #5365.

FWIW, I don't believe the behavior reported by @gazpachoking, even if it's reproducible, is a bug. 204 is an "OK" status response with no body. All request error (400-series) and server-error (500-series) responses are expected to have a body which explains the problem, and the HTTPException response is designed for these responses. So producing an HTTPException response with no body feels like a logic bug that neither FastAPI nor Starlette really needs to guard against.

Hmm, that's a good point. It isn't really an exception, although I still think it's nice to be able to short circuit the view function the same way you can for an actual exception.

@brotskydotcom
Copy link
Contributor

It isn't really an exception, although I still think it's nice to be able to short circuit the view function the same way you can for an actual exception.

I completely agree, and I agree that raise is very handy for this, so I'm glad this now works!

@seperman
Copy link

seperman commented Jan 6, 2023

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"}'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
answered bug Something isn't working reviewed
Projects
None yet
Development

No branches or pull requests

10 participants