Hacktoberfest 2026: những issue maintainer đã đánh dấu cho tháng Mười, đang mở và phù hợp người mới. Xem issue Hacktoberfest

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

Đang mở
#1,349 0 bình luận 0 reaction 2 người được giao Xem trên GitHub

Maintainer thường phản hồi trong vòng 2 ngày

@rohityan đang làm issue này rồi.

Từ ngày 16/10/2026.

Đánh giá

Issue này chưa được đánh giá.

Mô tả

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
Ngôn ngữ chính
Python
Star
2.2k
Fork
511
Merge trung bình
3 ngày 10 giờ
Pull request đã merge (30 ngày)
47

Chuẩn bị môi trường

Bắt đầu từ đâu

  1. Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
  2. Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
  3. Fork repository và làm thay đổi trên một nhánh.
  4. Mở pull request có tham chiếu số hiệu của issue.

Issue khác của a2aproject/a2a-python

Tất cả issue của a2aproject/a2a-python

Issue tương tự

Thêm issue về Python

Nhận issue mới trong hộp thư của bạn

Bản tóm tắt ngắn những issue GitHub phù hợp với người mới.