diff --git a/sentry_sdk/integrations/aiohttp.py b/sentry_sdk/integrations/aiohttp.py index a447b67f38..6a738f3af0 100644 --- a/sentry_sdk/integrations/aiohttp.py +++ b/sentry_sdk/integrations/aiohttp.py @@ -139,7 +139,17 @@ async def sentry_app_handle(self, request, *args, **kwargs): # have no way to tell. Do not set span status. reraise(*_capture_exception()) - transaction.set_http_status(response.status) + try: + # A valid response handler will return a valid response with a status. But, if the handler + # returns an invalid response (e.g. None), the line below will raise an AttributeError. + # Even though this is likely invalid, we need to handle this case to ensure we don't break + # the application. + response_status = response.status + except AttributeError: + pass + else: + transaction.set_http_status(response_status) + return response Application._handle = sentry_app_handle diff --git a/tests/integrations/aiohttp/test_aiohttp.py b/tests/integrations/aiohttp/test_aiohttp.py index 43e3bec546..be372b6643 100644 --- a/tests/integrations/aiohttp/test_aiohttp.py +++ b/tests/integrations/aiohttp/test_aiohttp.py @@ -596,3 +596,24 @@ async def hello(request): (event,) = events assert event["contexts"]["trace"]["origin"] == "auto.http.aiohttp" assert event["spans"][0]["origin"] == "auto.http.aiohttp" + + +@pytest.mark.asyncio +@pytest.mark.parametrize("invalid_response", (None, "invalid")) +async def test_invalid_response( + sentry_init, aiohttp_client, capture_events, invalid_response +): + sentry_init(integrations=[AioHttpIntegration()]) + + async def handler(_): + return invalid_response + + app = web.Application() + app.router.add_get("/", handler) + + client = await aiohttp_client(app) + + # Invalid response should result on a ServerDisconnectedError in the client side, not an internal server error. + # Important to note that the ServerDisconnectedError indicates we have no error server-side. + with pytest.raises(ServerDisconnectedError): + await client.get("/")