{"record":{"id":"7388ac8df0aa10ba","repo":"BerriAI/litellm","slug":"cannot-retry-a2a-localhost-url-fix-the-client-was","errorCode":null,"errorMessage":"Cannot retry A2A localhost URL fix: the client was not created by create_a2a_client, so no LiteLLM httpx client is attached.","messagePattern":"Cannot retry A2A localhost URL fix: the client was not created by create_a2a_client, so no LiteLLM httpx client is attached\\.","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"litellm/a2a_protocol/exception_mapping_utils.py","lineNumber":210,"sourceCode":"\n    request_type: Final = \"streaming \" if is_streaming else \"\"\n    verbose_logger.warning(\n        \"A2A %srequest to '%s' failed: %s. Agent card contains localhost/internal URL. Retrying with base_url '%s'.\",\n        request_type,\n        error.localhost_url,\n        error.original_error,\n        error.base_url,\n    )\n\n    # Fix the agent card URL\n    set_agent_card_url(agent_card, error.base_url)\n\n    # Reuse the httpx client and call context LiteLLM attached at creation, since the\n    # context carries this agent's trace-id/auth headers. Only clients built by\n    # ``create_a2a_client`` have them; an externally-supplied client cannot be retried.\n    httpx_client: Final = getattr(a2a_client, \"_litellm_httpx_client\", None)\n    if httpx_client is None:\n        raise RuntimeError(\n            \"Cannot retry A2A localhost URL fix: the client was not created by \"\n            \"create_a2a_client, so no LiteLLM httpx client is attached.\"\n        )\n\n    new_client: Final = await create_client(  # pyright: ignore[reportOptionalCall]\n        agent_card,\n        client_config=ClientConfig(  # pyright: ignore[reportOptionalCall]\n            httpx_client=httpx_client,\n            streaming=is_streaming,\n        ),\n    )\n    new_client._litellm_httpx_client = httpx_client\n    new_client._litellm_call_context = getattr(  # pyright: ignore[reportAttributeAccessIssue]  # LiteLLM-owned stash\n        a2a_client, \"_litellm_call_context\", None\n    )\n    new_client._litellm_agent_card = agent_card\n    return new_client\n","sourceCodeStart":192,"sourceCodeEnd":228,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/a2a_protocol/exception_mapping_utils.py#L192-L228","documentation":"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.","triggerScenarios":"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.","commonSituations":"Custom client tuning (timeouts, proxies, custom auth) leading teams to hand-roll the client; code written against older litellm versions that accepted arbitrary clients.","solutions":["Build the client with litellm.a2a_protocol.create_a2a_client(base_url=..., extra_headers=...) instead of the raw a2a-sdk builder","Or skip client management entirely: pass api_base (plus agent_extra_headers if needed) to asend_message/asend_message_streaming","If you need custom httpx behavior, check create_a2a_client's supported options (timeout, extra_headers) before building your own"],"exampleFix":"# before\nfrom a2a.client import ClientBuilder\nclient = ClientBuilder(httpx_client=my_httpx).build()\nawait asend_message(a2a_client=client, request=req)\n\n# after\nfrom litellm.a2a_protocol import create_a2a_client\nclient = await create_a2a_client(base_url='http://agent:10001', extra_headers={'Authorization': '...'})\nawait asend_message(a2a_client=client, request=req, api_base='http://agent:10001')","handlingStrategy":"type-guard","validationCode":"httpx_client = getattr(a2a_client, '_litellm_httpx_client', None)\nif httpx_client is None:\n    # not created by create_a2a_client: rebuild it the supported way\n    a2a_client = await create_a2a_client(\n        base_url=api_base,\n        extra_headers=agent_extra_headers,\n    )","typeGuard":"from typing import Any, TypeGuard\n\n\ndef is_litellm_managed_client(client: Any) -> TypeGuard[Any]:\n    \"\"\"Narrows to clients created by create_a2a_client, which carry the\n    shared litellm httpx client and support localhost-URL retries.\"\"\"\n    return getattr(client, '_litellm_httpx_client', None) is not None","tryCatchPattern":"try:\n    resp = await asend_message(a2a_client=my_client, request=req, api_base=api_base)\nexcept RuntimeError as e:\n    if 'create_a2a_client' in str(e):\n        my_client = await create_a2a_client(base_url=api_base)\n        resp = await asend_message(a2a_client=my_client, request=req, api_base=api_base)\n    else:\n        raise","preventionTips":["Treat create_a2a_client as the only supported client factory for litellm A2A calls","Push custom httpx needs (timeout, headers) through create_a2a_client's options","Regression-test your retry path, not just the happy path, when swapping clients"],"tags":["a2a","client","retry","httpx","runtime-error"],"backgroundTag":"external-client-not-supported","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}