- Notifications
You must be signed in to change notification settings - Fork96
Open
Description
Environment details
- OS type and version: macOS 26.1
- Python version: 3.12.12
- pip version: 25.3
google-api-coreversion: HEAD =f8bf6f9610f3e0e7580f223794c3906513e1fa73
Description
The retry mechanism clears the__cause__ attribute when re-raising non-retryable exceptions, breaking explicit exception chaining created withraise ... from ....
Steps to reproduce
- Use the
@retry_async.AsyncRetrydecorator - Raise a non-retryable exception with a cause
- Catch the exception and try to read or log its cause
Code example
importasynciofromgoogle.api_coreimportretry_async,exceptions@retry_async.AsyncRetry(predicate=retry_async.if_exception_type(exceptions.InternalServerError))asyncdefexample():try:raiseexceptions.Unauthenticated("Invalid credentials")exceptexceptions.Unauthenticatedasexc:raiseValueError("Access denied")fromexc# Explicit chainingasyncdefmain():# After going through retry:try:awaitexample()exceptValueErrorase:print(e.__cause__)# Expected: "401 Invalid credentials"; Actual: Noneif__name__=="__main__":asyncio.run(main())
Seehttps://github.com/googleapis/python-api-core/pull/879/changes for a unit test that fails with the current code and passes with the proposed fix.
Root Cause
Inretry_base.py:168,_default_exception_factory() returns(exc_list[-1], None), which causes raisefinal_exc fromNone on line 214, explicitly clearing__cause__.
Proposed fix
Preserve the exception'scause attribute:
# Beforereturnexc_list[-1],None# Afterfinal_exc=exc_list[-1]cause=getattr(final_exc,'__cause__',None)returnfinal_exc,cause
This maintains exception chains for better debugging and meets developer expectations for exception handling.
Draft pull request
Metadata
Metadata
Assignees
Labels
No labels