Skip to content

[Fix] A2a Agent Gateway Fixes - A2A agents deployed with localhost/internal URLs in their agent cards (e.g., http://0.0.0.0:8001/)#20604

Merged
ishaan-jaff merged 16 commits intomainfrom
litellm_a2a_fix_base_url
Feb 6, 2026
Merged

[Fix] A2a Agent Gateway Fixes - A2A agents deployed with localhost/internal URLs in their agent cards (e.g., http://0.0.0.0:8001/)#20604
ishaan-jaff merged 16 commits intomainfrom
litellm_a2a_fix_base_url

Conversation

@ishaan-jaff
Copy link
Member

@ishaan-jaff ishaan-jaff commented Feb 6, 2026

[Fix] A2a Agent Gateway Fixes - A2A agents deployed with localhost/internal URLs in their agent cards (e.g., http://0.0.0.0:8001/)

This PR fixes an issue where A2A agents deployed with localhost/internal URLs in their agent cards (e.g., http://0.0.0.0:8001/) cause connection failures. LiteLLM now automatically detects this misconfiguration and retries with the correct public URL.

Problem
Many A2A agents are deployed with development URLs left in their agent cards. When LiteLLM fetches the agent card and uses the URL specified in it, the request fails because localhost or 0.0.0.0 is not reachable from the client.
Customer-reported error:

a2a.client.errors.A2AClientHTTPError: HTTP Error 503: Network communication error: Cannot connect to host localhost:8000

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have Added testing in the tests/litellm/ directory, Adding at least 1 test is a hard requirement - see details
  • My PR passes all unit tests on make test-unit
  • My PR's scope is as isolated as possible, it only solves 1 specific problem

CI (LiteLLM team)

CI status guideline:

  • 50-55 passing tests: main is stable with minor issues.
  • 45-49 passing tests: acceptable but needs attention
  • <= 40 passing tests: unstable; be careful with your merges and assess the risk.
  • Branch creation CI run
    Link:

  • CI run for the last commit
    Link:

  • Merge / cherry-pick CI run
    Links:

Type

🆕 New Feature
✅ Test

Changes

@vercel
Copy link

vercel bot commented Feb 6, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Feb 6, 2026 11:01pm

Request Review

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Feb 6, 2026

Greptile Overview

Greptile Summary

  • Adds A2A-specific exception types plus mapping utilities to classify connection vs agent-card errors and trigger a localhost-URL retry.
  • Introduces helpers to detect localhost/internal URLs in agent cards and rewrite them to the original api_base for retry.
  • Updates asend_message and asend_message_streaming to retry once when a connection failure appears to be caused by a localhost URL embedded in the agent card.
  • Moves existing A2A agent tests under local_only_agent_tests and adds new agent retry integration tests plus unit tests for card URL helpers.

Confidence Score: 2/5

  • This PR is not safe to merge until the new tests and retry control flow issues are fixed.
  • Score reduced due to a guaranteed CI failure (env-dependent test without skip) and retry/error handling paths that can surface AssertionError or break due to reliance on private SDK internals / missing SDK guards.
  • litellm/a2a_protocol/main.py, litellm/a2a_protocol/exception_mapping_utils.py, tests/agent_tests/test_a2a_agent.py

Important Files Changed

Filename Overview
litellm/a2a_protocol/init.py Re-exported new A2A exception types from the package surface via all.
litellm/a2a_protocol/card_resolver.py Added localhost/internal URL detection and a helper to rewrite agent_card.url to the provided base_url; resolver fallback logic unchanged.
litellm/a2a_protocol/exception_mapping_utils.py Introduced exception mapping/retry helpers; contains reliance on A2AClient private attributes and potential None-call when a2a SDK missing.
litellm/a2a_protocol/exceptions.py Added custom A2A exception hierarchy with httpx.Response compatibility fields for LiteLLM error handling.
litellm/a2a_protocol/main.py Added retry-on-localhost URL logic for streaming and non-streaming calls; current control flow can fall through without a response and uses private client internals when recreating clients.
litellm/constants.py Added LOCALHOST_URL_PATTERNS and CONNECTION_ERROR_PATTERNS constants used by A2A retry/error mapping.
tests/agent_tests/local_only_agent_tests/local_vertex_agent.py Moved local Vertex agent script under local_only_agent_tests; no functional changes.
tests/agent_tests/local_only_agent_tests/test_a2a.py Moved existing A2A agent tests to local_only_agent_tests; no functional changes.
tests/agent_tests/local_only_agent_tests/test_a2a_completion_bridge.py Moved existing completion bridge tests to local_only_agent_tests; no functional changes.
tests/agent_tests/test_a2a_agent.py Added env-dependent integration tests but does not skip when A2A_AGENT_URL is unset, so it will fail in CI by passing None api_base.
tests/test_litellm/a2a_protocol/test_card_resolver.py Added unit tests for localhost URL detection and card URL rewriting helpers.

Sequence Diagram

sequenceDiagram
  participant U as Caller
  participant LM as litellm.a2a_protocol.main
  participant CC as create_a2a_client
  participant CR as LiteLLMA2ACardResolver
  participant A2A as a2a.client.A2AClient
  participant AG as A2A Agent

  U->>LM: asend_message(request, api_base)
  alt no a2a_client provided
    LM->>CC: create_a2a_client(base_url=api_base)
    CC->>CR: get_agent_card()
    CR->>AG: GET /.well-known/agent-card.json
    alt 404 / fails
      CR->>AG: GET /.well-known/agent.json
    end
    CR-->>CC: AgentCard(url=card.url)
    CC->>A2A: new A2AClient(httpx_client, agent_card)
    A2A-->>LM: a2a_client
  end

  LM->>A2A: send_message(request)
  alt connection error + agent_card.url is localhost
    A2A--x LM: Exception
    LM->>LM: map_a2a_exception(e, card_url, api_base)
    LM-->>LM: raises A2ALocalhostURLError
    LM->>LM: handle_a2a_localhost_retry()
    LM->>A2A: new A2AClient(agent_card.url := api_base)
    LM->>A2A: send_message(request) (retry)
  else success
    A2A-->>LM: SendMessageResponse
  end

  LM-->>U: LiteLLMSendMessageResponse
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

11 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

Comment on lines +258 to +266
# Retry loop: if connection fails due to localhost URL in agent card, retry with fixed URL
a2a_response = None
for _ in range(2): # max 2 attempts: original + 1 retry
try:
a2a_response = await a2a_client.send_message(request)
break # success, exit retry loop
except A2ALocalhostURLError as e:
# Localhost URL error - fix and retry
a2a_client = handle_a2a_localhost_retry(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Retry loop can fall through

In asend_message(), the for _ in range(2) loop can complete without setting a2a_response (e.g., first attempt raises A2ALocalhostURLError, you retry, and the second attempt raises another A2ALocalhostURLError; that except block doesn’t continue/raise, so the loop ends and hits assert a2a_response is not None). This will surface as an AssertionError instead of a meaningful A2A exception. The loop should deterministically re-raise the last mapped exception on the final attempt.

@ishaan-jaff ishaan-jaff merged commit b78f4c9 into main Feb 6, 2026
13 of 39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant