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

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions