newrelic-python-agent: Issue running starlette: TypeError: _run_asgi2() takes 2 positional arguments but 4 were given

I am getting the following traceback when trying to launch an asgi server with starlette: I am using this run command: newrelic-admin run-program gunicorn -w 4 -k uvicorn.workers.UvicornWorker my_package:app

Traceback (most recent call last):
  File "/app/.heroku/python/lib/python3.8/site-packages/uvicorn/protocols/http/httptools_impl.py", line 391, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/app/.heroku/python/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/app/.heroku/python/lib/python3.8/site-packages/newrelic/api/asgi_application.py", line 387, in nr_async_asgi
    return await coro
  File "/app/.heroku/python/lib/python3.8/site-packages/newrelic/common/async_proxy.py", line 148, in __next__
    return self.send(None)
  File "/app/.heroku/python/lib/python3.8/site-packages/newrelic/common/async_proxy.py", line 120, in send
    return self.__wrapped__.send(value)
  File "/app/.heroku/python/lib/python3.8/site-packages/newrelic/api/asgi_application.py", line 387, in nr_async_asgi
    return await coro
  File "/app/.heroku/python/lib/python3.8/site-packages/newrelic/common/async_proxy.py", line 148, in __next__
    return self.send(None)
  File "/app/.heroku/python/lib/python3.8/site-packages/newrelic/common/async_proxy.py", line 120, in send
    return self.__wrapped__.send(value)
  File "/app/.heroku/python/lib/python3.8/site-packages/starlette/applications.py", line 111, in __call__
    await self.middleware_stack(scope, receive, send)
  File "<string>", line 5, in wrapper
  File "/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py", line 181, in __call__
    raise exc from None
  File "/app/.heroku/python/lib/python3.8/site-packages/starlette/middleware/errors.py", line 159, in __call__
    await self.app(scope, receive, _send)
  File "/app/.heroku/python/lib/python3.8/site-packages/newrelic/hooks/framework_starlette.py", line 108, in middleware_wrapper
    return await FunctionTraceWrapper(wrapped, name=name)(*args, **kwargs)
  File "/app/.heroku/python/lib/python3.8/site-packages/newrelic/api/function_trace.py", line 150, in literal_wrapper
    return wrapped(*args, **kwargs)
TypeError: _run_asgi2() takes 2 positional arguments but 4 were given

Using:

newrelic==5.24.0.153
starlette==0.13.8

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 4
  • Comments: 32 (12 by maintainers)

Most upvoted comments

Why has this been marked as wontfix? We are having the same issue and even trying the alternative method (here: https://docs.newrelic.com/docs/apm/agents/python-agent/installation/python-agent-advanced-integration/#manual-integration ) we have exactly the same error. Basically we have no way to use New Relic in our application because we are also using Sentry.

This is still an issue for us with similar circumstances.

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Is this truly a wontfix? We are currently running into this with one of our FastAPI projects, and don’t want to have to make a choice between NewRelic and Sentry.

This is still an issue for us too, while we are using ASGI middleware for sentry and New Relic as monitoring service.

@cancan101 While we investigate, you should be able to work around this issue by forcing all requests to be treated as ASGI v3 by adding this patch:

SentryAsgiMiddleware._run_asgi2 = SentryAsgiMiddleware._run_asgi3

This forces the _run_asgi2 call to run the _run_asgi3 method instead.

Example app:

from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route


async def pong(request):
    return PlainTextResponse("*")


routes = [
    Route("/", pong),
]
app = Starlette(routes=routes)


@app.middleware("http")
async def middleware_with_decorator(request, call_next):
    response = await call_next(request)
    return response


SentryAsgiMiddleware._run_asgi2 = SentryAsgiMiddleware._run_asgi3
app.add_middleware(SentryAsgiMiddleware)

still an open issue. don’t want to drop newrelic.

I think sentry-python v1.8.0 release fixes this!

Should add one comment. The reason that wrapt gives the correct results is that inspect.iscoroutinefunction() and inspec.iscoroutine() internally use isinstance() checks. With wrapt isinstance() checks actually work against the original wrapped type, as well as the wrapper, so the lookups done for subsequent to those checks appears to work. So my original comments saying they likely wouldn’t work was wrong just because of how they were implemented.

Neither the wrapt ObjectProxy or FunctionWrapper which derives from it have any support for proxying async functions. Thus if using the inspect functions on a wrapper and you ask whether the wrapped function is async, you will get the wrong result if it is.

The callable(), inspect.iscoroutine() and inspect.iscoroutinefunction() functions are problematic in a proxy object because if you wrap something which isn’t any of them, yet add the proxy functions, the tests on the wrapper will always say the wrapped object is a function or async function even if they aren’t.

This is why the base ObjectProxy doesn’t have a proxy for __call__() and if you know you are wrapping a function you must use FunctionWrapper which does. There is no separate AsyncFunctionWrapper object for async functions, but even if there was it would be complicated to know when to use it versus a normal function wrapper in a generic system where over versions of the software being wrapped, what is used can change.

The only possible solution have thought might be able to use to resolve the problem is to have the constructor for the proxy object perform a check whether a normal function, async function is passed and add extra proxy functions to the wrapper instance in that case for async.

Complicating this though is that you also have async generators and async context managers, so you need to look for the presence of each specific possible async dunder method and create a proxy function for each one as found. This will create a bit of overhead on construction and overhead may mount up over time if done on the generic project.

For FunctionWrapper at least, since should know only dealing with a function, it may be enough to look for just __await__() and add a proxy function for it to the wrapper instance. I have no idea right now how async instance methods complicate this though.

So suggest adding to the newrelic FunctionWrapper equivalent a constructor which checks for __await__ on what is being wrapped, and if there is, add a proxy function for it. Not sure if this is enough though for async instance methods as may need to be add support down in wrapt directly in the special _FunctionWrapperBase and BoundFuntionWrapper types.

Anyway, since not really been devoting any time to improving wrapt, I have been ignoring the whole async issues for a very long time now. So this isn’t the only related issue.

Ability to put time in on this is a bit complicated right now.

I am also using this middleware which seems to be causing the issue: https://github.com/mfreeborn/fastapi-sqlalchemy/blob/39e7e8bb5d98a9de5fc365adf4bdc661d824b614/fastapi_sqlalchemy/middleware.py#L18-L45 I have it added after sentry in the list.