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
RuntimeError: Stream consumed when reading body with startlette (bug since 1.9.10) #1675
Comments
Hey @sevaho Thanks for reporting this. We will have a look when there is time. (Honestly I thought I have fixed this already...) |
Thanks @antonpirker ! Looks like there is more to it as well.
Whatever file you pass, the content for |
Downgrading to |
I had about 20k errors caused by this since yesterday and I was off. Downgraded the library once I figured this out. Lost a lot of API calls. Is there a way to reset quota on sentry because this bug was essentially caused due to sentry library issue? |
Hello! I will have a look at this first thing on Monday! |
I was (unplanned so) afk Monday and Tuesday, so I am looking into this now. |
This is happening because of some inconsistencies on how Starlette (the foundation of FastAPI) is dealing with request data: I am now investigating if this should be fixed in Starlette/FastAPI or in Sentry: |
Considering this has been the way I am thinking maybe it'd be an option to work around the caching issue by getting it consumed into # sentry_sdk/integrations/starlette.py
from starlette.datastructures import FormData
from starlette.formparsers import FormParser, MultiPartParser
async def form(self):
# type: (StarletteRequestExtractor) -> typing.Optional[typing.Mapping[str, typing.Any]]
if multipart is None:
return None
# Parse the body first to get it cached, as Starlette does not cache form() as it
# does with body() and json() https://github.com/encode/starlette/discussions/1933
await self.request.body()
return await self.request.form() EDIT: PR by @antonpirker following this approach #1724 Thanks! 🙏 |
Spent a couple of hours debugging this issue until stumbling upon this ticket. Had zero suspicion that it's Sentry causing it. I was so confused: "how could the body stream be already consumed?". Turns out, the only way to get around it is to downgrade FastAPI or to turn off Sentry. Here is the piece of code causing this issue: @app.post(
"/html",
)
async def post_html_resource(
request: Request,
):
data = await request.body() Where |
I think this issue can be marked as solved, as it's been fixed in version 1.11.0 (it's working for me at least). Thanks for your work @antonpirker! 🙇 |
This appears to still be an issue for us in v1.11.1. Not sure if we are doing something wrong. Will investigate further. |
Yeah I am seeing this as well in v1.11.1 |
having the issue as well in v1.13.0 . Can we please consider re-opening it? |
Is this fixed in v1.19.x? |
:( |
Seeing this in 1.25.1 and not really understanding the circumstances that cause it. Anyone got a useful link? |
To everyone commenting that this is still broken, please ensure you are not using any other middlewares, as it is not just Sentry that would cause this issue. Any middleware which reads from the request's @caner-cetin if you are not even using this library, then this is definitely not the place you should be commenting. Try commenting on an existing issue in the Starlette or FastAPI repos, or making your own in those repos. |
I wanted to come back here to say that the issue I experienced was actually due to exception handling middleware I wrote that should've checked whether the Request had been disconnected. FastAPI will catch when the Request stream is disconnected, which eventually gets raised as a The solution to this is to check for instances of Leaving this example here for posterity, as I imagine lots of other people will get confused seeing sentry in the stacktrace if they do something similar, as this is a common approach to writing a generic FastAPI middleware. class CustomRouteHandler(APIRoute):
def get_route_handler(self) -> Callable:
original_route_handler = super().get_route_handler()
async def handle_route(request: Request) -> Response:
try:
response = await original_route_handler(request)
except Exception as exc:
if not await request.is_disconnected():
# ... handle non-disconnect errors
response = ApiErrorResponse(error=ErrorDetails(code=..., message='...'))
return response |
@acnebs Yep, I am sorry for my needless outrage, problem was in MinIO Python SDK, it was reading the stream and not rewinding after read, leaving me with errors when trying to read the same file after uploading to MinIO. Sorry again! <3 |
How do you use Sentry?
Sentry Saas (sentry.io)
Version
1.9.10
Steps to Reproduce
Server:
Run with uvicorn:
As client do:
Expected Result
To succeed.
Actual Result
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/uvicorn/protocols/http/h11_impl.py", line 404, in run_asgi
result = await app( # type: ignore[func-returns-value]
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in call
return await self.app(scope, receive, send)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/fastapi/applications.py", line 270, in call
await super().call(scope, receive, send)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py", line 293, in _sentry_patched_asgi_app
return await middleware(scope, receive, send)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/sentry_sdk/integrations/asgi.py", line 138, in _run_asgi3
return await self._run_app(scope, lambda: self.app(scope, receive, send))
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/sentry_sdk/integrations/asgi.py", line 187, in _run_app
raise exc from None
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/sentry_sdk/integrations/asgi.py", line 182, in _run_app
return await callback()
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/applications.py", line 124, in call
await self.middleware_stack(scope, receive, send)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py", line 98, in _create_span_call
await old_call(*args, **kwargs)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/middleware/errors.py", line 184, in call
raise exc
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/middleware/errors.py", line 162, in call
await self.app(scope, receive, _send)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py", line 191, in _sentry_exceptionmiddleware_call
await old_call(self, scope, receive, send)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py", line 98, in _create_span_call
await old_call(*args, **kwargs)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 79, in call
raise exc
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/middleware/exceptions.py", line 68, in call
await self.app(scope, receive, sender)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py", line 98, in _create_span_call
await old_call(*args, **kwargs)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 21, in call
raise e
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/fastapi/middleware/asyncexitstack.py", line 18, in call
await self.app(scope, receive, send)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/routing.py", line 706, in call
await route.handle(scope, receive, send)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/routing.py", line 276, in handle
await self.app(scope, receive, send)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/routing.py", line 66, in app
response = await func(request)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/sentry_sdk/integrations/fastapi.py", line 106, in _sentry_app
return await old_app(*args, **kwargs)
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/fastapi/routing.py", line 231, in app
raw_response = await run_endpoint_function(
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/fastapi/routing.py", line 160, in run_endpoint_function
return await dependant.call(**values)
File "/home/sevaho/gitlab/centurion/./app.py", line 14, in read_root
r = await request.body()
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/requests.py", line 234, in body
async for chunk in self.stream():
File "/home/sevaho/.cache/pypoetry/virtualenvs/centurion-X1KYOsH6-py3.10/lib/python3.10/site-packages/starlette/requests.py", line 215, in stream
raise RuntimeError("Stream consumed")
RuntimeError: Stream consumed
The text was updated successfully, but these errors were encountered: