BerriAI/litellm · error · RuntimeError

Cannot retry A2A localhost URL fix: the client was not…

Error message

Cannot retry A2A localhost URL fix: the client was not created by create_a2a_client, so no LiteLLM httpx client is attached.

What it means

During a localhost-URL retry, LiteLLM reuses the shared httpx client it attached to the a2a client at creation time (the _litellm_httpx_client attribute), because it carries this agent's trace-id and auth headers. An externally constructed a2a-sdk client lacks that attribute, so the retry aborts with this RuntimeError rather than silently losing header context. Only clients built by litellm.a2a_protocol.create_a2a_client are retryable.

Solutions

  1. Build the client with litellm.a2a_protocol.create_a2a_client(base_url=..., extra_headers=...) instead of the raw a2a-sdk builder
  2. Or skip client management entirely: pass api_base (plus agent_extra_headers if needed) to asend_message/asend_message_streaming
  3. If you need custom httpx behavior, check create_a2a_client's supported options (timeout, extra_headers) before building your own

Example fix

# before
from a2a.client import ClientBuilder
client = ClientBuilder(httpx_client=my_httpx).build()
await asend_message(a2a_client=client, request=req)

# after
from litellm.a2a_protocol import create_a2a_client
client = await create_a2a_client(base_url='http://agent:10001', extra_headers={'Authorization': '...'})
await asend_message(a2a_client=client, request=req, api_base='http://agent:10001')
Defensive patterns

Strategy: type-guard

Validate before calling

httpx_client = getattr(a2a_client, '_litellm_httpx_client', None)
if httpx_client is None:
    # not created by create_a2a_client: rebuild it the supported way
    a2a_client = await create_a2a_client(
        base_url=api_base,
        extra_headers=agent_extra_headers,
    )

Type guard

from typing import Any, TypeGuard


def is_litellm_managed_client(client: Any) -> TypeGuard[Any]:
    """Narrows to clients created by create_a2a_client, which carry the
    shared litellm httpx client and support localhost-URL retries."""
    return getattr(client, '_litellm_httpx_client', None) is not None

Try / catch

try:
    resp = await asend_message(a2a_client=my_client, request=req, api_base=api_base)
except RuntimeError as e:
    if 'create_a2a_client' in str(e):
        my_client = await create_a2a_client(base_url=api_base)
        resp = await asend_message(a2a_client=my_client, request=req, api_base=api_base)
    else:
        raise

Prevention

When it happens

Trigger: Passing a self-built a2a-sdk client (e.g. ClientBuilder(...).build()) to asend_message/asend_message_streaming, then hitting the localhost-card retry path: the rewritten card cannot be used because no LiteLLM httpx client is attached.

Common situations: Custom client tuning (timeouts, proxies, custom auth) leading teams to hand-roll the client; code written against older litellm versions that accepted arbitrary clients.

Related errors


AI-assisted analysis of BerriAI/litellm@77b7c6c40c (2026-08-18). Data as JSON: /api/errors/7388ac8df0aa10ba. Report an issue: GitHub.

Appendix: source

Thrown at litellm/a2a_protocol/exception_mapping_utils.py:210

    request_type: Final = "streaming " if is_streaming else ""
    verbose_logger.warning(
        "A2A %srequest to '%s' failed: %s. Agent card contains localhost/internal URL. Retrying with base_url '%s'.",
        request_type,
        error.localhost_url,
        error.original_error,
        error.base_url,
    )

    # Fix the agent card URL
    set_agent_card_url(agent_card, error.base_url)

    # Reuse the httpx client and call context LiteLLM attached at creation, since the
    # context carries this agent's trace-id/auth headers. Only clients built by
    # ``create_a2a_client`` have them; an externally-supplied client cannot be retried.
    httpx_client: Final = getattr(a2a_client, "_litellm_httpx_client", None)
    if httpx_client is None:
        raise RuntimeError(
            "Cannot retry A2A localhost URL fix: the client was not created by "
            "create_a2a_client, so no LiteLLM httpx client is attached."
        )

    new_client: Final = await create_client(  # pyright: ignore[reportOptionalCall]
        agent_card,
        client_config=ClientConfig(  # pyright: ignore[reportOptionalCall]
            httpx_client=httpx_client,
            streaming=is_streaming,
        ),
    )
    new_client._litellm_httpx_client = httpx_client
    new_client._litellm_call_context = getattr(  # pyright: ignore[reportAttributeAccessIssue]  # LiteLLM-owned stash
        a2a_client, "_litellm_call_context", None
    )
    new_client._litellm_agent_card = agent_card
    return new_client

View on GitHub (pinned to 77b7c6c40c)