Skip to content

[Bug]: A2AClient instance may lack agent_card attribute despite successful card fetch (HTTP 200) #122

@maildata02-fe

Description

@maildata02-fe

What happened?

Describe the Bug

When using A2AClient.get_client_from_agent_card_url to initialize a client for an A2A agent, the method sometimes returns an A2AClient instance that does not have the agent_card attribute set (or its value is None). This occurs even when the underlying HTTP GET request to fetch the agent card's JSON from the target agent's /.well-known/agent.json endpoint returns a 200 OK status and valid JSON. Consequently, attempting to access client_instance.agent_card leads to an AttributeError.

Steps to Reproduce

  1. Have a specialist A2A agent running and serving a valid AgentCard JSON at its /.well-known/agent.json endpoint. An example of such AgentCard JSON is provided below.
  2. From a separate orchestrator or client process, attempt to initialize an A2AClient for this specialist agent using the following pattern:
    import asyncio
    import httpx
    import logging
    from a2a.client import A2AClient
    from a2a.client.errors import A2AClientHTTPError, A2AClientError # For exception handling
    from a2a.types import AgentCard # Ensure AgentCard is imported
    
    # Configure basic logging
    logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    logger = logging.getLogger("sdk_test")
    
    async def attempt_to_get_client(specialist_url: str):
        async with httpx.AsyncClient(timeout=15.0) as http_client:
            try:
                logger.info(f"Attempting to fetch and process agent card from: {specialist_url}")
                
                # Attempt to get the client
                client_instance = await A2AClient.get_client_from_agent_card_url(
                    http_client, specialist_url
                )
    
                logger.info(f"Call to get_client_from_agent_card_url for '{specialist_url}' returned: {client_instance}")
                logger.info(f"Type of returned object: {type(client_instance)}")
    
                if isinstance(client_instance, A2AClient):
                    logger.info(f"Instance attributes (__dict__): {client_instance.__dict__}")
                    if hasattr(client_instance, 'agent_card') and client_instance.agent_card:
                        logger.info(f"Successfully obtained client with agent_card. Name: {client_instance.agent_card.name}")
                        # Further validation of client_instance.agent_card (e.g., type) could be added
                        if isinstance(client_instance.agent_card, AgentCard):
                            logger.info("client_instance.agent_card is an instance of AgentCard.")
                        else:
                            logger.warning(f"client_instance.agent_card is NOT an instance of AgentCard. Type: {type(client_instance.agent_card)}")
                    else:
                        logger.error("ERROR: A2AClient instance IS missing 'agent_card' attribute or agent_card is None.")
                else:
                    logger.error(f"ERROR: Call to get_client_from_agent_card_url did not return an A2AClient instance. Got: {type(client_instance)}")
    
            except A2AClientHTTPError as e:
                logger.error(f"A2AClientHTTPError for {specialist_url}: {e}")
            except A2AClientError as e: # Catch other specific A2A client errors
                logger.error(f"A2AClientError for {specialist_url}: {e}", exc_info=True)
            except Exception as e:
                logger.error(f"Unexpected error for {specialist_url}: {e}", exc_info=True)
    
    async def main():
        # Replace with the actual URL of your specialist agent serving the card
        specialist_agent_url = "http://localhost:9998" 
        await attempt_to_get_client(specialist_agent_url)
    
    if __name__ == "__main__":
        asyncio.run(main())
  3. Observe the logs.

Example Specialist AgentCard Content

The following AgentCard JSON is successfully fetched (HTTP 200 OK) from http://localhost:9998/.well-known/agent.json but still leads to the issue:

{
    "capabilities": {
        "pushNotifications": false,
        "streaming": false,
        "functionCalling": true
    },
    "defaultInputModes": [
        "text"
    ],
    "defaultOutputModes": [
        "text"
    ],
    "description": "An AI assistant using LangChain for tool integration.",
    "name": "LangChain Tool-Powered AI",
    "skills": [
        {
            "description": "Can chat, answer questions, check leap years, and perform calculations using integrated tools.",
            "examples": [
                "Is 2024 a leap year?",
                "Calculate (100 / 5) + 3",
                "What's the weather like?"
            ],
            "id": "chat_and_lc_tools",
            "name": "Chat & LangChain Tools",
            "tags": [
                "general",
                "tools",
                "langchain",
                "calculator",
                "leap year"
            ]
        }
    ],
    "url": "http://localhost:9998/",
    "version": "1.0.0"
}

Log Output Example

Logs from the client code above, demonstrating the issue:

INFO:root:Attempting to fetch and process agent card from: http://localhost:9998
INFO:httpx:HTTP Request: GET http://localhost:9998/.well-known/agent.json "HTTP/1.1 200 OK"
INFO:root:Call to get_client_from_agent_card_url for 'http://localhost:9998' returned: <a2a.client.client.A2AClient object at 0x...>
INFO:root:Type of returned object: <class 'a2a.client.client.A2AClient'>
INFO:root:Instance attributes (__dict__): {'url': 'http://localhost:9998/', 'httpx_client': <httpx.AsyncClient object at 0x...>}
ERROR:root:ERROR: A2AClient instance IS missing 'agent_card' attribute or agent_card is None.

Expected Behavior

After A2AClient.get_client_from_agent_card_url successfully fetches and presumably parses the agent card (indicated by the HTTP 200 OK and no immediate parsing exceptions from the SDK), the returned A2AClient instance should consistently have a populated agent_card attribute. This attribute should hold the AgentCard object created from the fetched JSON data.

Actual Behavior

The HTTP GET request to fetch the card from the specialist agent is successful. An A2AClient object is returned. However, this A2AClient instance does not have an agent_card attribute in its __dict__ (or agent_card is None), as confirmed by logging client_instance.__dict__ and hasattr(client_instance, 'agent_card'). The instance only has 'url': 'http://specialist-agent:9998/' and 'httpx_client': <httpx.AsyncClient object at 0x7f426eb11c10>

Relevant log output

Code of Conduct

  • I agree to follow this project's Code of Conduct

Metadata

Metadata

Labels

No labels
No labels

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions