Skip to content

Uploading conversions async - AttributeError: 'UnaryUnaryCall' object has no attribute 'exception' #1059

Description

@antonmyronyuk

Describe the bug:
Sometimes upload_click_conversions async call (from the client.get_service('ConversionUploadService', is_async=True)) failed with the AttributeError: 'UnaryUnaryCall' object has no attribute 'exception'

Steps to Reproduce:
Unable to reproduce locally, encountered this issue only in production (15 errors per 77k successful requests). Looks like this happens on timeouts or google ads service temporary unavailability

Expected behavior:
No error or some error specific to Google Ads, not generic AttributeError

Client library version and API version:
Client library version: 29.1.0
Google Ads API version: v23
Python version: 3.12

Python deps (all the deps from the project):

aiohttp==3.9.5
aiosignal==1.4.0
annotated-types==0.7.0
anyio==4.6.0
attrs==25.4.0
CacheControl==0.14.4
certifi==2026.2.25
cffi==2.0.0
charset-normalizer==3.4.4
click==8.3.1
cryptography==46.0.5
dnspython==2.8.0
email_validator==2.2.0
facade-webonboarding-proto==0.0.224
fastapi==0.115.0
firebase-admin==6.0.0
frozenlist==1.8.0
google-ads==29.1.0
google-api-core==2.29.0
google-api-python-client==2.190.0
google-auth==2.48.0
google-auth-httplib2==0.3.0
google-auth-oauthlib==1.3.0
google-cloud-core==2.5.0
google-cloud-firestore==2.23.0
google-cloud-storage==3.9.0
google-crc32c==1.8.0
google-resumable-media==2.8.0
googleapis-common-protos==1.72.0
grpc-gateway-protoc-gen-openapiv2==0.1.0
grpcio==1.62.3
grpcio-status==1.62.3
grpcio-tools==1.62.3
h11==0.16.0
httpcore==1.0.9
httplib2==0.31.2
httptools==0.6.1
httpx==0.27.0
idna==3.11
iso4217parse-fixed==0.1.0
msgpack==1.1.2
multidict==6.7.1
oauthlib==3.3.1
orjson==3.10.14
prometheus_client==0.21.0
propcache==0.4.1
proto-plus==1.27.1
protobuf==4.25.5
pyasn1==0.6.2
pyasn1_modules==0.4.2
pycparser==3.0
pydantic==2.9.2
pydantic-settings==2.5.2
pydantic_core==2.23.4
PyJWT==2.10.1
pyparsing==3.3.2
python-dateutil==2.9.0.post0
python-dotenv==1.2.2
python-logstash==0.4.8
PyYAML==6.0.3
redis==5.1.1
requests==2.32.3
requests-oauthlib==2.0.0
rsa==4.9.1
sentry-sdk==2.25.1
setuptools==82.0.0
six==1.17.0
sniffio==1.3.1
starlette==0.38.6
stripe==11.1.0
tenacity==9.0.0
typing_extensions==4.15.0
uritemplate==4.2.0
urllib3==2.6.3
uvicorn==0.31.0
uvloop==0.20.0
yarl==1.23.0

Request/Response Logs:

Anything else we should know about your project / environment:
Code used for sending events:

client = GoogleAdsClientSync.load_from_string(yaml_str, version="v23")
request = client.get_type('UploadClickConversionsRequest')
request.customer_id = ...
request.conversions.append(...)
request.partial_failure = True
conversion_upload_service = client.get_service('ConversionUploadService', is_async=True)
response = await conversion_upload_service.upload_click_conversions(request, timeout=10)  # error occurred here
if response.partial_failure_error.message:
    raise SomeError(response.partial_failure_error.message)

Screenshot from Sentry:
Image

Activity

  1. added
    bugSomething isn't working
    triageNew issue; requires attention
    on Mar 3, 2026
  2. bobhancockg commented on Mar 3, 2026

    @bobhancockg
    Contributor

    Root Cause
    In the asynchronous implementation of the Google Ads Python client library, the grpc.aio.Call objects (such as UnaryUnaryCall) do not expose an .exception() method, unlike their synchronous counterparts. The library's internal ExceptionInterceptor and LoggingInterceptor were incorrectly attempting to call this method on asynchronous call objects, leading to the reported AttributeError, especially during failure scenarios like timeouts or invalid requests.

    Resolution

    1. ExceptionInterceptor:
      • Modify _handle_grpc_failure_async to accept an optional exception argument.
      • Update _AsyncUnaryUnaryCallWrapper and _AsyncUnaryStreamCallWrapper to catch grpc.RpcError during await or iteration and pass the caught exception object directly to the failure handler.
      • Add safe fallback logic in _handle_grpc_failure_async to handle cases where the exception object might still be missing.
    2. LoggingInterceptor:
      • Refine _log_request_async to safely check for the existence of the .exception() method.
      • For asynchronous calls missing the method, the interceptor should attempt to retrieve the exception by awaiting the call (which is safe as the call is already marked as done) or by inspecting the status code.

    These changes should ensure that asynchronous errors are correctly caught, wrapped into GoogleAdsException, and logged without crashing the client.

  3. BenRKarl commented on Mar 16, 2026

    @BenRKarl
    Contributor

    @antonmyronyuk do you have the full stack trace for this?

  4. antonmyronyuk commented on Mar 17, 2026

    @antonmyronyuk
    Author

    @BenRKarl, sure
    This method was called inside the FastAPI service, so the traceback is pretty long

    Traceback (most recent call last):
      File "/usr/local/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 401, in run_asgi
        result = await app(  # type: ignore[func-returns-value]
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
        return await self.app(scope, receive, send)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
        await super().__call__(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 408, in _sentry_patched_asgi_app
        return await middleware(scope, receive, send)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/asgi.py", line 158, in _run_asgi3
        return await self._run_app(scope, receive, send, asgi_version=3)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/asgi.py", line 259, in _run_app
        raise exc from None
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/asgi.py", line 254, in _run_app
        return await self.app(
               ^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/applications.py", line 113, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 187, in __call__
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 165, in __call__
        await self.app(scope, receive, _send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 185, in __call__
        with collapse_excgroups():
             ^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__
        self.gen.throw(value)
      File "/usr/local/lib/python3.12/site-packages/starlette/_utils.py", line 83, in collapse_excgroups
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 187, in __call__
        response = await self.dispatch_func(request, call_next)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/app/reface/utils/middlewares.py", line 35, in dispatch
        return await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 163, in call_next
        raise app_exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 149, in coro
        await self.app(scope, receive_or_disconnect, send_no_error)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 93, in __call__
        await self.simple_response(scope, receive, send, request_headers=headers)
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 144, in simple_response
        await self.app(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 185, in __call__
        with collapse_excgroups():
             ^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__
        self.gen.throw(value)
      File "/usr/local/lib/python3.12/site-packages/starlette/_utils.py", line 83, in collapse_excgroups
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 187, in __call__
        response = await self.dispatch_func(request, call_next)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/app/reface/utils/middlewares.py", line 29, in dispatch
        return await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 163, in call_next
        raise app_exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 149, in coro
        await self.app(scope, receive_or_disconnect, send_no_error)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 297, in _sentry_exceptionmiddleware_call
        await old_call(self, scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
        await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app
        await app(scope, receive, sender)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 715, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 735, in app
        await route.handle(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 288, in handle
        await self.app(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 76, in app
        await wrap_app_handling_exceptions(app, request)(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app
        await app(scope, receive, sender)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 73, in app
        response = await f(request)
                   ^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/fastapi.py", line 143, in _sentry_app
        return await old_app(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/fastapi/routing.py", line 301, in app
        raw_response = await run_endpoint_function(
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/fastapi/routing.py", line 212, in run_endpoint_function
        return await dependant.call(**values)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/app/reface/views/analytics.py", line 91, in google_ads_upload_conversion
        await google_ads_client.upload_offline_conversion(
      File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 189, in async_wrapped
        return await copy(fn, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 111, in __call__
        do = await self.iter(retry_state=retry_state)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 153, in iter
        result = await action(retry_state)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/tenacity/_utils.py", line 99, in inner
        return call(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/tenacity/__init__.py", line 398, in <lambda>
        self._add_action_func(lambda rs: rs.outcome.result())
                                         ^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/concurrent/futures/_base.py", line 449, in result
        return self.__get_result()
               ^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/concurrent/futures/_base.py", line 401, in __get_result
        raise self._exception
      File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 114, in __call__
        result = await fn(*args, **kwargs)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/app/reface/services/google_ads.py", line 112, in upload_offline_conversion
        response = await conversion_upload_service.upload_click_conversions(request, timeout=10)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/google/ads/googleads/v23/services/services/conversion_upload_service/async_client.py", line 421, in upload_click_conversions
        response = await rpc(
                   ^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/google/api_core/grpc_helpers_async.py", line 86, in __await__
        response = yield from self._call.__await__()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/grpc/aio/_interceptor.py", line 471, in __await__
        response = yield from call.__await__()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/google/ads/googleads/interceptors/exception_interceptor.py", line 163, in __await__
        yield from self._interceptor._handle_grpc_failure_async(self._call).__await__()
      File "/usr/local/lib/python3.12/site-packages/google/ads/googleads/interceptors/exception_interceptor.py", line 267, in _handle_grpc_failure_async
        response_exception = response.exception()
                             ^^^^^^^^^^^^^^^^^^
    AttributeError: 'UnaryUnaryCall' object has no attribute 'exception'
    
  5. BenRKarl commented on Mar 18, 2026

    @BenRKarl
    Contributor

    Thanks @antonmyronyuk - question, do you get a similar error for other types of failed requests? For example, if you intentionally set an invalid customer ID, or specify a bad endpoint in your configuration.

    I ask because I'm unable to reproduce this error, and I'm wondering if there's something special about the timeout errors you received (which are hard to reproduce) or the environment where this is happening.

  6. antonmyronyuk commented on Mar 19, 2026

    @antonmyronyuk
    Author

    @BenRKarl , the only way I've reproduced this issue:

    • up the service
    • made a few successful requests
    • revoked the token (we use yaml file for google ads configuration with developer_token, use_proto_plus (true), client_id, client_secret and refresh_token keys)
    • made another request and got 2 errors (in logs):
    Traceback (most recent call last):
      File "/usr/local/lib/python3.12/site-packages/grpc/_plugin_wrapping.py", line 105, in __call__
        self._metadata_plugin(
      File "/usr/local/lib/python3.12/site-packages/google/auth/transport/grpc.py", line 93, in __call__
        callback(self._get_authorization_headers(context), None)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/google/auth/transport/grpc.py", line 79, in _get_authorization_headers
        self._credentials.before_request(
      File "/usr/local/lib/python3.12/site-packages/google/auth/credentials.py", line 230, in before_request
        self._blocking_refresh(request)
      File "/usr/local/lib/python3.12/site-packages/google/auth/credentials.py", line 193, in _blocking_refresh
        self.refresh(request)
      File "/usr/local/lib/python3.12/site-packages/google/oauth2/credentials.py", line 412, in refresh
        ) = reauth.refresh_grant(
            ^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/google/oauth2/reauth.py", line 370, in refresh_grant
        _client._handle_error_response(response_data, retryable_error)
      File "/usr/local/lib/python3.12/site-packages/google/oauth2/_client.py", line 69, in _handle_error_response
        raise exceptions.RefreshError(
    google.auth.exceptions.RefreshError: ('invalid_grant: Token has been expired or revoked.', {'error': 'invalid_grant', 'error_description': 'Token has been expired or revoked.'})
    
    Traceback (most recent call last):
      File "/usr/local/lib/python3.12/site-packages/google/ads/googleads/interceptors/logging_interceptor.py", line 505, in _log_request_async
        result = await response
                 ^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/grpc/aio/_call.py", line 318, in __await__
        raise _create_rpc_error(
    grpc.aio._call.AioRpcError: <AioRpcError of RPC that terminated with:
    	status = StatusCode.UNAVAILABLE
    	details = "Getting metadata from plugin failed with error: ('invalid_grant: Token has been expired or revoked.', {'error': 'invalid_grant', 'error_description': 'Token has been expired or revoked.'})"
    	debug_error_string = "UNKNOWN:Error received from peer  {grpc_message:"Getting metadata from plugin failed with error: (\'invalid_grant: Token has been expired or revoked.\', {\'error\': \'invalid_grant\', \'error_description\': \'Token has been expired or revoked.\'})", grpc_status:14, created_time:"2026-03-18T11:36:24.430019357+00:00"}"
    >
    

    And in the main FastAPI service an AttributeError was raised:

    Traceback (most recent call last):
      File "/usr/local/lib/python3.12/site-packages/uvicorn/protocols/http/httptools_impl.py", line 401, in run_asgi
        result = await app(  # type: ignore[func-returns-value]
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/uvicorn/middleware/proxy_headers.py", line 60, in __call__
        return await self.app(scope, receive, send)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/fastapi/applications.py", line 1054, in __call__
        await super().__call__(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 408, in _sentry_patched_asgi_app
        return await middleware(scope, receive, send)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/asgi.py", line 158, in _run_asgi3
        return await self._run_app(scope, receive, send, asgi_version=3)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/asgi.py", line 259, in _run_app
        raise exc from None
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/asgi.py", line 254, in _run_app
        return await self.app(
               ^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/applications.py", line 113, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 187, in __call__
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/errors.py", line 165, in __call__
        await self.app(scope, receive, _send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 185, in __call__
        with collapse_excgroups():
             ^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__
        self.gen.throw(value)
      File "/usr/local/lib/python3.12/site-packages/starlette/_utils.py", line 83, in collapse_excgroups
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 187, in __call__
        response = await self.dispatch_func(request, call_next)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/app/bitepal/utils/middlewares.py", line 35, in dispatch
        return await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 163, in call_next
        raise app_exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 149, in coro
        await self.app(scope, receive_or_disconnect, send_no_error)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 93, in __call__
        await self.simple_response(scope, receive, send, request_headers=headers)
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/cors.py", line 144, in simple_response
        await self.app(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 185, in __call__
        with collapse_excgroups():
             ^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/contextlib.py", line 158, in __exit__
        self.gen.throw(value)
      File "/usr/local/lib/python3.12/site-packages/starlette/_utils.py", line 83, in collapse_excgroups
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 187, in __call__
        response = await self.dispatch_func(request, call_next)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/app/bitepal/utils/middlewares.py", line 29, in dispatch
        return await call_next(request)
               ^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 163, in call_next
        raise app_exc
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/base.py", line 149, in coro
        await self.app(scope, receive_or_disconnect, send_no_error)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 297, in _sentry_exceptionmiddleware_call
        await old_call(self, scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/starlette.py", line 199, in _create_span_call
        return await old_call(app, scope, new_receive, new_send, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
        await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app
        await app(scope, receive, sender)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 715, in __call__
        await self.middleware_stack(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 735, in app
        await route.handle(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 288, in handle
        await self.app(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 76, in app
        await wrap_app_handling_exceptions(app, request)(scope, receive, send)
      File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 62, in wrapped_app
        raise exc
      File "/usr/local/lib/python3.12/site-packages/starlette/_exception_handler.py", line 51, in wrapped_app
        await app(scope, receive, sender)
      File "/usr/local/lib/python3.12/site-packages/starlette/routing.py", line 73, in app
        response = await f(request)
                   ^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/sentry_sdk/integrations/fastapi.py", line 143, in _sentry_app
        return await old_app(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/fastapi/routing.py", line 301, in app
        raw_response = await run_endpoint_function(
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/fastapi/routing.py", line 212, in run_endpoint_function
        return await dependant.call(**values)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/app/bitepal/views/analytics.py", line 235, in google_ads_upload_conversion
        await google_ads_client.upload_offline_conversion(
      File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 189, in async_wrapped
        return await copy(fn, *args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 111, in __call__
        do = await self.iter(retry_state=retry_state)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 153, in iter
        result = await action(retry_state)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/tenacity/_utils.py", line 99, in inner
        return call(*args, **kwargs)
               ^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/tenacity/__init__.py", line 418, in exc_check
        raise retry_exc.reraise()
              ^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/tenacity/__init__.py", line 185, in reraise
        raise self.last_attempt.result()
              ^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/concurrent/futures/_base.py", line 449, in result
        return self.__get_result()
               ^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/concurrent/futures/_base.py", line 401, in __get_result
        raise self._exception
      File "/usr/local/lib/python3.12/site-packages/tenacity/asyncio/__init__.py", line 114, in __call__
        result = await fn(*args, **kwargs)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/app/bitepal/services/google_ads.py", line 112, in upload_offline_conversion
        response = await conversion_upload_service.upload_click_conversions(request, timeout=10)
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/google/ads/googleads/v23/services/services/conversion_upload_service/async_client.py", line 421, in upload_click_conversions
        response = await rpc(
                   ^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/google/api_core/grpc_helpers_async.py", line 86, in __await__
        response = yield from self._call.__await__()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/grpc/aio/_interceptor.py", line 471, in __await__
        response = yield from call.__await__()
                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
      File "/usr/local/lib/python3.12/site-packages/google/ads/googleads/interceptors/exception_interceptor.py", line 163, in __await__
        yield from self._interceptor._handle_grpc_failure_async(self._call).__await__()
      File "/usr/local/lib/python3.12/site-packages/google/ads/googleads/interceptors/exception_interceptor.py", line 267, in _handle_grpc_failure_async
        response_exception = response.exception()
                             ^^^^^^^^^^^^^^^^^^
    AttributeError: 'UnaryUnaryCall' object has no attribute 'exception'
    

    ⚠️ Note, token should be active on service start. If it is revoked from the beginning, normal google.auth.exceptions.RefreshError error is raises, not the neededAttributeError

  7. bobhancockg commented on Mar 19, 2026

    @bobhancockg
    Contributor
  8. BenRKarl commented on Mar 20, 2026

    @BenRKarl
    Contributor

    Just FYI I believe this PR (#1061) addresses this issue, however there are some things in the PR that still need to be addressed, namely failing unit tests. I would also like to reproduce the error myself, which I haven't been able to do, so that I can be sure that we're making the right fix.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingtriageNew issue; requires attention

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions