Hacktoberfest 2026 : les issues que les mainteneurs ont marquées pour octobre, ouvertes et accessibles aux débutants. Parcourir les issues Hacktoberfest

[Bug]: Closing one client from a ClientFactory closes the shared httpx client for every other client

Ouverte
#1,349 0 commentaires 0 réactions 2 personnes assignées Voir sur GitHub

Les mainteneurs répondent en général sous 3 jours

@Iwaniukooo11 y travaille déjà.

Depuis le 19/10/2026.

Évaluation

Cette issue n'a pas encore été évaluée.

Description

What happened?

Since #947, ClientFactory creates a single httpx.AsyncClient in __init__ (src/a2a/client/client_factory.py) and passes it to every transport it creates, and to the card resolver in create_from_url:

httpx_client = config.httpx_client or httpx.AsyncClient()
...
self._httpx_client = httpx_client

Each HTTP transport closes that client in close() (JsonRpcTransport.close, RestTransport.close and the v0.3 compat transports):

async def close(self) -> None:
    """Closes the httpx client."""
    await self.httpx_client.aclose()

So closing one client, explicitly or by leaving async with client:, closes the HTTP client shared by every other client from the same factory, and by the factory itself. Before #947 each transport got its own httpx.AsyncClient() unless one was passed in ClientConfig, so this did not happen.

Reproduction:

factory = ClientFactory()
client_a = factory.create(card_a)
client_b = factory.create(card_b)

async with client_a:
    ...

await client_b.get_task(GetTaskRequest(id='task-1'))
# RuntimeError: Cannot send a request, as the client has been closed.

await factory.create_from_url('https://agent-c.example')
# RuntimeError: Cannot send a request, as the client has been closed.

The error is a raw RuntimeError from httpx, not an A2AClientError, so it is also not caught by code handling SDK errors. The create_client docstring suggests using ClientFactory directly "for reusing a factory across multiple agents", which is the case that breaks.

Expected: closing a client releases only its own resources. Other clients from the same factory, and the factory itself, keep working.

I see two possible directions, depending on whether sharing the connection pool across clients was a goal of #947:

Option A: one HTTP client per created client (as before #947). When ClientConfig.httpx_client is not set, each create() builds its own httpx.AsyncClient (with the same default A2A-Version header), and create_from_url resolves the card with its own short-lived client. Small change, no new API, same behavior as before #947.

Option B: the factory owns the shared client. Transports stop closing an HTTP client they did not create, and ClientFactory gets close() and async with support to release the shared client. Keeps a single connection pool, but adds API surface and makes the factory responsible for its lifetime.

In both cases, a client passed through ClientConfig.httpx_client would be left to the caller to close. Today the SDK closes it too, which was already the case before #947, so I'm happy to keep that out of scope if you prefer.

Happy to open a PR with either option, or with a different approach if you have one in mind. Just let me know which direction you prefer.

Code of Conduct
  • I agree to follow this project's Code of Conduct
Langage dominant
Python
Étoiles
2.2k
Forks
511
Merge moyen
4 j 1 h
PR mergées (30 j)
40

Préparer son environnement

Par où commencer

  1. Lisez l'issue en entier, puis le guide de contribution du projet.
  2. Signalez en commentaire que vous la prenez — cela évite que deux personnes fassent le même travail.
  3. Forkez le dépôt et travaillez sur une branche.
  4. Ouvrez une pull request qui référence le numéro de l'issue.

Autres issues de a2aproject/a2a-python

Toutes les issues de a2aproject/a2a-python

Issues similaires

Plus d'issues Python

Recevez les nouvelles issues par e-mail

Un résumé court des issues GitHub adaptées aux débutants.