ItemsRequestBuilder.request_adapter.send_async raises AttributeError: 'NoneType' object has no attribute '__aenter__'
Hello,
I have a scenario, where I use GraphServiceClient based on ClientSecretCredential to paginate over some site-list-items resources, and after an hour or so, the aiohttp session is becoming None.
Excerpt from the exception:
### Beginning of stack trace ###
[Some previous code of mine...]
[Some previous code of SDK...]
File "/src/pkgs/azure/identity/aio/_credentials/client_secret.py", line 71, in _request_token
return await self._client.obtain_token_by_client_secret(scopes, self._secret, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
...
File "/src/pkgs/azure/core/pipeline/transport/_aiohttp.py", line 127, in open
await self.session.__aenter__()
^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute '__aenter__'"
### End of stack trace ###
The pagination is done inside a loop, using the odata_next_link which is returned with the response, thus requesting the next page.
Eventually, after about an hour, the SDK tries to renew the token but fails with the exception, described further below.
I dig deep into the source code to detect if I did something wrong, but I didn't find anything. I have a few thoughts but I don't want to confuse you guys or mislead, so I'll just describe my code and the full exception stack trace.
My setup
Python versoin: 3.11.5
msgraph version, from pdm lock file:
[[package]]
name = "msgraph-sdk"
version = "1.2.0"
requires_python = ">=3.8"
summary = "The Microsoft Graph Python SDK"
dependencies = [
"azure-identity>=1.12.0",
"microsoft-kiota-abstractions<2.0.0,>=1.0.0",
"microsoft-kiota-authentication-azure<2.0.0,>=1.0.0",
"microsoft-kiota-http<2.0.0,>=1.0.0",
"microsoft-kiota-serialization-form>=0.1.0",
"microsoft-kiota-serialization-json<2.0.0,>=1.0.0",
"microsoft-kiota-serialization-multipart>=0.1.0",
"microsoft-kiota-serialization-text<2.0.0,>=1.0.0",
"msgraph-core>=1.0.0",
]
files = [
{file = "msgraph-sdk-1.2.0.tar.gz", hash = "sha256:689eec74fcb5cb29446947e4761fa57edeeb3ec1dccd7975c44d12d8d9db9c4f"},
{file = "msgraph_sdk-1.2.0-py3-none-any.whl", hash = "sha256:4a9f706413c0a497cdfffd0b741122a5e73206333d566d115089cef9f4adadb7"},
]
My code flow
My code flow, in short, is as such:
# In some consts file
ERROR_MAPPING = {
"4XX": ODataError,
"5XX": ODataError,
}
ALL_ADMIN_SITES_LIST = "DO_NOT_DELETE_SPLIST_TENANTADMIN_ALL_SITES_AGGREGATED_SITECOLLECTIONS"
credential = ClientSecretCredential(some_tenant_id, some_app_id, some_app_secret)
graph_client = GraphServiceClient(credentials=credential, scopes=some_scopes)
def sites_generator():
while is_first_run or next_url is not None:
is_first_run = False
request_builder = graph_client.sites.by_site_id(SOME_SITE_ID).lists.by_list_id(ALL_ADMIN_SITES_LIST).items
request_information = request_builder.to_get_request_information()
if next_url is not None:
request_information.url = next_url
response = await request_builder.request_adapter.send_async(request_information, SiteCollectionResponse, ERROR_MAPPING)
# will be None once there are no further pages
next_url = page.odata_next_link
yield response
async for item in sites_generator():
try:
# do something with response
except Exception:
# On any exception, we continue to loop!
### End of my code ###
The exception stack trace
And the exception I receive, seems to be during the token "refresh". I double-quote "refresh" because the SDK doesn't use the refresh token but just requests a new one using the obtain_token_by_client_secret function.
Traceback (most recent call last):
[Some previous code of mine...]
File "/src/pkgs/common/services/o365/msgraph/paginator/paginator.py", line 30, in get_page
await request_builder.request_adapter.send_async(request_information, collection_response_type, ERROR_MAPPING)
File "/src/pkgs/kiota_http/httpx_request_adapter.py", line 178, in send_async
response = await self.get_http_response_message(request_info, parent_span)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/kiota_http/httpx_request_adapter.py", line 523, in get_http_response_message
await self._authentication_provider.authenticate_request(
File "/src/pkgs/kiota_abstractions/authentication/base_bearer_token_authentication_provider.py", line 50, in authenticate_request
token = await self.access_token_provider.get_authorization_token(
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/kiota_authentication_azure/azure_identity_access_token_provider.py", line 106, in get_authorization_token
result = await result
^^^^^^^^^^^^
File "/src/pkgs/azure/identity/aio/_internal/get_token_mixin.py", line 86, in get_token
token = await self._request_token(*scopes, claims=claims, tenant_id=tenant_id, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/identity/aio/_credentials/client_secret.py", line 71, in _request_token
return await self._client.obtain_token_by_client_secret(scopes, self._secret, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/identity/aio/_internal/aad_client.py", line 48, in obtain_token_by_client_secret
return await self._run_pipeline(request, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/identity/aio/_internal/aad_client.py", line 84, in _run_pipeline
response = await self._pipeline.run(request, retry_on_methods=self._POST, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/core/pipeline/_base_async.py", line 221, in run
return await first_node.send(pipeline_request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/core/pipeline/_base_async.py", line 69, in send
response = await self.next.send(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/core/pipeline/_base_async.py", line 69, in send
response = await self.next.send(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/core/pipeline/_base_async.py", line 69, in send
response = await self.next.send(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Previous line repeated 1 more time]
File "/src/pkgs/azure/core/pipeline/policies/_retry_async.py", line 179, in send
response = await self.next.send(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/core/pipeline/_base_async.py", line 69, in send
response = await self.next.send(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/core/pipeline/_base_async.py", line 69, in send
response = await self.next.send(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/core/pipeline/_base_async.py", line 69, in send
response = await self.next.send(request)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
[Previous line repeated 1 more time]
File "/src/pkgs/azure/core/pipeline/_base_async.py", line 106, in send
await self._sender.send(request.http_request, **request.context.options),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/src/pkgs/azure/core/pipeline/transport/_aiohttp.py", line 231, in send
await self.open()
File "/src/pkgs/azure/core/pipeline/transport/_aiohttp.py", line 127, in open
await self.session.__aenter__()
^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute '__aenter__'
Would love you help on this!
Thanks!
Hello, this could be due to a limitation on the token lifetime caused by CAE, https://github.com/Azure/azure-sdk-for-python/blob/48cdbb2d3f6422a74faca22a12adb41c26dd475e/sdk/core/azure-core/azure/core/credentials_async.py#L21, To fix this, we have on our side put the flag to true, so tokens can live for upto 24 hours.
Upgrading to the latest release of https://github.com/microsoft/kiota-authentication-azure-python/releases/tag/v1.1.0 should fix this
This issue has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment.