Skip to content

Raise exception from background task on BaseHTTPMiddleware introduced a bug? #2893

@ramannanda9

Description

@ramannanda9

Looking at this commit, it pushed the logic out to the end, but if there is an error raised and handled, it will reraise it again

Steps to reproduce.

   @app.get("/error")
    async def foo():
        exc = Exception("foobar")
        exc.status_code = 501
        raise exc

Middleware handler.

@app.middleware("http")
    async def http_middleware(request: Request, call_next):
          try:
                response = await call_next(request)
          except Exception as exc:
                logger.exception("Failure in user code!")
                # stuff in between
                response = JSONResponse(
                {"error": str(exc), "stacktrace": traceback.format_exc()},
                status_code=status_code,
            )
          return response      

trace

During handling of the above exception, another exception occurred:

request = <starlette.middleware.base._CachedRequest object at 0x13a837040>

    async def call_next(request: Request) -> Response:
        async def receive_or_disconnect() -> Message:
            if response_sent.is_set():
                return {"type": "http.disconnect"}
    
            async with anyio.create_task_group() as task_group:
    
                async def wrap(func: typing.Callable[[], typing.Awaitable[T]]) -> T:
                    result = await func()
                    task_group.cancel_scope.cancel()
                    return result
    
                task_group.start_soon(wrap, response_sent.wait)
                message = await wrap(wrapped_receive)
    
            if response_sent.is_set():
                return {"type": "http.disconnect"}
    
            return message
    
        async def send_no_error(message: Message) -> None:
            try:
                await send_stream.send(message)
            except anyio.BrokenResourceError:
                # recv_stream has been closed, i.e. response_sent has been set.
                return
    
        async def coro() -> None:
            nonlocal app_exc
    
            with send_stream:
                try:
                    await self.app(scope, receive_or_disconnect, send_no_error)
                except Exception as exc:
                    app_exc = exc
    
        task_group.start_soon(coro)
    
        try:
>           message = await recv_stream.receive()


_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = MemoryObjectReceiveStream(_state=MemoryObjectStreamState(max_buffer_size=0, buffer=deque([]), open_send_channels=0, open_receive_channels=0, waiting_receivers=OrderedDict(), waiting_senders=OrderedDict()), _closed=True)

    async def receive(self) -> T_co:
        await checkpoint()
        try:
            return self.receive_nowait()
        except WouldBlock:
            # Add ourselves in the queue
            receive_event = Event()
            receiver = MemoryObjectItemReceiver[T_co]()
            self._state.waiting_receivers[receive_event] = receiver
    
            try:
                await receive_event.wait()
            finally:
                self._state.waiting_receivers.pop(receive_event, None)
    
            try:
                return receiver.item
            except AttributeError:
>               raise EndOfStream
E               anyio.EndOfStream

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions