Skip to content

[Bug] AsyncKiotaTransport does not cleanup resources correctly #494

@intx4

Description

@intx4

Is your feature request related to a problem? Please describe.
AsyncKiotaTransport might not be cleaning up resources correctly.

class AsyncKiotaTransport(httpx.AsyncBaseTransport):
    """A custom transport that implements Kiota middleware functionality
    """

    def __init__(self, transport: httpx.AsyncBaseTransport, pipeline: MiddlewarePipeline) -> None:
        self.transport = transport
        self.pipeline = pipeline

    async def handle_async_request(self, request: httpx.Request) -> httpx.Response:
        if self.pipeline:
            response = await self.pipeline.send(request)
            return response

        response = await self.transport.handle_async_request(request)
        return response

AsyncKiotaTransport inherits methods like __aexit__ and aclose from the AsyncBaseTransport. Then, KiotaClientFactory uses _load_middleware_to_client and replaces the underlying _transport of the client with its AsyncKiotaTransport. Later calls to httpx.AsyncClient.aclose() will try and call client._transport.aclose() which will render it a no-op.

 def _load_middleware_to_client(
        client: httpx.AsyncClient, middleware: Optional[list[BaseMiddleware]]
    ) -> httpx.AsyncClient:
        current_transport = client._transport
        client._transport = KiotaClientFactory._replace_transport_with_custom_kiota_transport(
            current_transport, middleware
        )
        if client._mounts:
            mounts: dict = {}
            for pattern, transport in client._mounts.items():
                if transport is None:
                    mounts[pattern] = None
                else:
                    mounts[pattern
                           ] = KiotaClientFactory._replace_transport_with_custom_kiota_transport(
                               transport, middleware
                           )
            client._mounts = dict(sorted(mounts.items()))
        return client

Describe the solution you'd like
AsyncKiotaTransport should in turn properly cleanup the self.transport resource calling its aclose() method

Describe alternatives you've considered
NA
Additional context
We discovered the issue as part of an internal investigation regarding connection leaks on a Flask server hosting an agentic pipeline with Langgraph. We invoked a graph asynchronously with a kiota client (that was properly closed in code), but yet we are noticing connection leaks. We would like the opinion of the maintainers on the matter

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Needs Triage 🔍

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions