Skip to content

Starlette integration raises TypeError if an unhandled exception has a status_code that isn't an integer #2062

@bruceduhamel

Description

@bruceduhamel

How do you use Sentry?

Sentry Saas (sentry.io)

Version

1.21.1

Steps to Reproduce

  1. Setup a FastAPI app
  2. Configure Sentry with the StarletteIntegration and FastApiIntegration
  3. Create a custom exception class MyError with attribute status_code
  4. Create a FastAPI endpoint that raises MyError with status_code='N/A'
  5. Call the FastAPI endpoint

Expected Result

MyError caught by FastAPI error handler.

The error in my application is specific to the opensearchpy package available on pypi. The ConnectionTimeout class has a status_code that may be an int or a string. It is required to be "N/A" if no status code is available, which is the case for client side connection timeouts.

When this error bubbles its way up to the Sentry Starlette integration to be reported as an error, it finds the status_code field and checks to see if it is a 500 error. This comparison fails because there is no valid status code, instead, it ends up comparing "N/A" >= 500, thus leading to a TypeError.

I would suggest the Starlette integration be updated to verify the status_code is an instance of int before trying to compare it to one. This will handle cases where status code may be a string "N/A", None, or something else.

Actual Result

An unhandled TypeError is raised from the Sentry patched Starlette exception handler

../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/testclient.py:590: in post
    return super().post(
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/httpx/_client.py:1132: in post
    return self.request(
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/testclient.py:465: in request
    return super().request(
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/httpx/_client.py:814: in request
    return self.send(request, auth=auth, follow_redirects=follow_redirects)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/httpx.py:65: in send
    rv = real_send(self, request, **kwargs)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/httpx/_client.py:901: in send
    response = self._send_handling_auth(
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/httpx/_client.py:929: in _send_handling_auth
    response = self._send_handling_redirects(
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/httpx/_client.py:966: in _send_handling_redirects
    response = self._send_single_request(request)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/httpx/_client.py:1002: in _send_single_request
    response = transport.handle_request(request)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/testclient.py:342: in handle_request
    raise exc
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/testclient.py:339: in handle_request
    portal.call(self.app, scope, receive, send)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/anyio/from_thread.py:283: in call
    return cast(T_Retval, self.start_task_soon(func, *args).result())
/opt/local/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/concurrent/futures/_base.py:458: in result
    return self.__get_result()
/opt/local/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/concurrent/futures/_base.py:403: in __get_result
    raise self._exception
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/anyio/from_thread.py:219: in _call_func
    retval = await retval
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/fastapi/applications.py:276: in __call__
    await super().__call__(scope, receive, send)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py:333: in _sentry_patched_asgi_app
    return await middleware(scope, receive, send)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/asgi.py:139: in _run_asgi3
    return await self._run_app(scope, lambda: self.app(scope, receive, send))
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/asgi.py:188: in _run_app
    raise exc from None
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/asgi.py:183: in _run_app
    return await callback()
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/applications.py:122: in __call__
    await self.middleware_stack(scope, receive, send)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py:130: in _create_span_call
    return await old_call(app, scope, new_receive, new_send, **kwargs)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/middleware/errors.py:184: in __call__
    raise exc
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/middleware/errors.py:162: in __call__
    await self.app(scope, receive, _send)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/asgi.py:139: in _run_asgi3
    return await self._run_app(scope, lambda: self.app(scope, receive, send))
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/asgi.py:149: in _run_app
    raise exc from None
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/asgi.py:146: in _run_app
    return await callback()
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/middleware/cors.py:84: in __call__
    await self.app(scope, receive, send)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/middleware/gzip.py:24: in __call__
    await responder(scope, receive, send)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/middleware/gzip.py:44: in __call__
    await self.app(scope, receive, self.send_with_gzip)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py:227: in _sentry_exceptionmiddleware_call
    await old_call(self, scope, receive, send)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py:130: in _create_span_call
    return await old_call(app, scope, new_receive, new_send, **kwargs)
../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/starlette/middleware/exceptions.py:88: in __call__
    response = await handler(request, exc)
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 

self = <starlette.requests.Request object at 0x1070bd540>
args = (ConnectionTimeout('N/A', 'Timeout', 'Read timed out'),), kwargs = {}
exp = ConnectionTimeout('N/A', 'Timeout', 'Read timed out')

    async def _sentry_patched_exception_handler(self, *args, **kwargs):
        # type: (Any, Any, Any) -> None
        exp = args[0]
    
        is_http_server_error = (
>           hasattr(exp, "status_code") and exp.status_code >= 500
        )
E       TypeError: '>=' not supported between instances of 'str' and 'int'

../../../../.virtualenvs/my_api-tckt/lib/python3.10/site-packages/sentry_sdk/integrations/starlette.py:186: TypeError

Metadata

Metadata

Assignees

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions