Repository navigation
Description
What happened?
Hello everyone,
I am trying to do an example of an A2A client-server communication with APIKeySecurityScheme authentication.
My pydantic version is 2.11.3 and the a2a library version is the 0.2.5.
This is my agent card specification in the server:
agent_card = AgentCard(
name='Carpenter Agent',
description='This agent handles carpentry tasks using tools like hammer and screwdriver.',
url=f'http://{host}:{port}/',
version='1.0.0',
defaultInputModes=CarpenterAgent.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=CarpenterAgent.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=skills,
securitySchemes={
"api-key": {
"description":"the description of the security schema",
"in":In.header,
"name":"passed_api_key",
"type":"apiKey"}
}
) The problem is in the A2ACardResolver when it retrieves the card with the get_agent_card() method, as the server sends the 'in' parameter in the securityScheme like 'in_' (using the 'model_dump' from pydantic) so the model_validate() in the client cannot parse it and throws the error showed in the log.
The solution I've managed is to remove the Field in the 'in_' types definition for APIKeySecurityScheme:
class APIKeySecurityScheme(BaseModel):
"""
API Key security scheme.
"""
description: str | None = None
"""
Description of this security scheme.
"""
in_: In
"""
The location of the API key. Valid values are "query", "header", or "cookie".
"""
name: str
"""
The name of the header, query or cookie parameter to be used.
"""
type: Literal['apiKey'] = 'apiKeyAnd having my new card like this:
agent_card = AgentCard(
name='Carpenter Agent',
description='This agent handles carpentry tasks using tools like hammer and screwdriver.',
url=f'http://{host}:{port}/',
version='1.0.0',
defaultInputModes=CarpenterAgent.SUPPORTED_CONTENT_TYPES,
defaultOutputModes=CarpenterAgent.SUPPORTED_CONTENT_TYPES,
capabilities=capabilities,
skills=skills,
securitySchemes={
"api-key": {
"description":"the description of the security schema",
"in_":In.header,
"name":"passed_api_key",
"type":"apiKey"}
}
)Should I add it as a new PR or there is an existant solution?
Thank you so much!
Relevant log output
a2a.client.errors.A2AClientJSONError: JSON Error: Failed to validate agent card structure from http://localhost:8080/.well-known/agent.json: [{"type":"missing","loc":["securitySchemes","api-key","APIKeySecurityScheme","in"],"msg":"Field required","input":{"description":"the description of the security schema","in_":"header","name":"passed_api_key","type":"apiKey"},"url":"https://errors.pydantic.dev/2.11/v/missing"},{"type":"missing","loc":["securitySchemes","api-key","HTTPAuthSecurityScheme","scheme"],"msg":"Field required","input":{"description":"the description of the security schema","in_":"header","name":"passed_api_key","type":"apiKey"},"url":"https://errors.pydantic.dev/2.11/v/missing"},{"type":"literal_error","loc":["securitySchemes","api-key","HTTPAuthSecurityScheme","type"],"msg":"Input should be 'http'","input":"apiKey","ctx":{"expected":"'http'"},"url":"https://errors.pydantic.dev/2.11/v/literal_error"},{"type":"missing","loc":["securitySchemes","api-key","OAuth2SecurityScheme","flows"],"msg":"Field required","input":{"description":"the description of the security schema","in_":"header","name":"passed_api_key","type":"apiKey"},"url":"https://errors.pydantic.dev/2.11/v/missing"},{"type":"literal_error","loc":["securitySchemes","api-key","OAuth2SecurityScheme","type"],"msg":"Input should be 'oauth2'","input":"apiKey","ctx":{"expected":"'oauth2'"},"url":"https://errors.pydantic.dev/2.11/v/literal_error"},{"type":"missing","loc":["securitySchemes","api-key","OpenIdConnectSecurityScheme","openIdConnectUrl"],"msg":"Field required","input":{"description":"the description of the security schema","in_":"header","name":"passed_api_key","type":"apiKey"},"url":"https://errors.pydantic.dev/2.11/v/missing"},{"type":"literal_error","loc":["securitySchemes","api-key","OpenIdConnectSecurityScheme","type"],"msg":"Input should be 'openIdConnect'","input":"apiKey","ctx":{"expected":"'openIdConnect'"},"url":"https://errors.pydantic.dev/2.11/v/literal_error"}]Code of Conduct
- I agree to follow this project's Code of Conduct
Activity
[-][Bug]:[/-][+][Bug]: APIKeySecurityScheme implementation parsing in the client[/+]idgu-dev commentedon Jun 11, 2025
Hi, i am also facing the same issue. Looking forward to this bug to be fixed
holtskinner commentedon Jun 13, 2025
Hello! Thank you for the excellent and detailed bug report. This is a fantastic catch and highlights a subtle but important aspect of working with Pydantic models that have field aliases for reserved keywords.
The good news is that the SDK's type definitions are correct, and this can be resolved with a small adjustment in how the
AgentCardis created on your server, without needing any changes to the SDK itself.The Explanation
The core of the issue lies in the difference between a Python object's attribute name and its JSON alias, especially for reserved keywords like
in.Why the SDK uses
in_: Becauseinis a reserved keyword in Python, it cannot be used directly as a variable or attribute name. The standard practice (as recommended by PEP 8) is to append a trailing underscore, making itin_. This is what we've done in theAPIKeySecuritySchememodel.The Role of
alias='in': TheField(..., alias='in')part in the model is the bridge between the Python world and the JSON world. It tells Pydantic:in, use its value to populate the Python attribute namedin_." (This is what happens on the client side).in_attribute and put it under a key namedin." (This is what happens on the server side when it serves the agent card).The Fix
The problem is in how the
APIKeySecuritySchemedictionary is defined in your server-side Python code. When you are creating a Pydantic model instance from a Python dictionary, you must use the Python attribute names, not the JSON aliases.Your current code uses the JSON alias
"in"as a dictionary key:When Pydantic validates this, it doesn't recognize the
inkey for instantiation and therefore raises a validation error because the requiredin_field is missing. Whenmodel_dumpis later called on the server, it correctly uses the alias and sends"in"over the wire, which is what the client expects.The fix is to use the Python attribute name
in_when defining the dictionary on your server:Or you can just use the
APIKeySecuritySchemeclass instead of adict, which should map to the correct field.With this change, your server will correctly instantiate the
AgentCardmodel. Then, when theA2ACardResolveron the client side requests the card, your server will serialize the model to JSON, correctly converting thein_attribute back to an"in"key, which the client will then parse successfully.Your proposed solution of removing the alias works, but it would make the SDK non-compliant with the A2A specification, which requires the on-the-wire JSON key to be
"in".Thank you again for flagging this! It's a very common point of confusion with Pydantic, and your report helps clarify it for everyone. No PR is needed for the SDK itself.
alberto-atk commentedon Jun 16, 2025
Hello again.
I've tried several things, including the two options that you mentioned in the examples, but it is still not working:
Test 1: Object + keyword argument
Error in server:
validated_self = self.pydantic_validator.validate_python(data, self_instance=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 1 validation error for APIKeySecurityScheme
in
Field required [type=missing, input_value={'description': 'the desc..._key', 'type': 'apiKey'}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.11/v/missing
Test 2: Object + without keyword argument
Error in server:
"api-key": APIKeySecurityScheme(
^^^^^^^^^^^^^^^^^^^^^
TypeError: BaseModel.init() takes 1 positional argument but 5 were given
Test 3: Object + dict init with in_
Error in server:
pydantic_core._pydantic_core.ValidationError: 1 validation error for APIKeySecurityScheme
in
Field required [type=missing, input_value={'description': 'the desc..._key', 'type': 'apiKey'}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.11/v/missing
Test 4: Object + dict init with in
Error in client:
a2a.client.errors.A2AClientJSONError: JSON Error: Failed to validate agent card structure from http://localhost:8080/.well-known/agent.json: [{"type":"missing","loc":["securitySchemes","api-key","APIKeySecurityScheme","in"],"msg":"Field required","input":{"description":"the description of the security schema","in_":"header","name":"passed_api_key","type":"apiKey"},"url":"https://errors.pydantic.dev/2.11/v/missing"},{"type":"missing","loc":["securitySchemes","api-key","HTTPAuthSecurityScheme","scheme"],"msg":"Field required","input":{"description":"the description of the security schema","in_":"header","name":"passed_api_key","type":"apiKey"},"url":"https://errors.pydantic.dev/2.11/v/missing"},{"type":"literal_error","loc":["securitySchemes","api-key","HTTPAuthSecurityScheme","type"],"msg":"Input should be 'http'","input":"apiKey","ctx":{"expected":"'http'"},"url":"https://errors.pydantic.dev/2.11/v/literal_error"},{"type":"missing","loc":["securitySchemes","api-key","OAuth2SecurityScheme","flows"],"msg":"Field required","input":{"description":"the description of the security schema","in_":"header","name":"passed_api_key","type":"apiKey"},"url":"https://errors.pydantic.dev/2.11/v/missing"},{"type":"literal_error","loc":["securitySchemes","api-key","OAuth2SecurityScheme","type"],"msg":"Input should be 'oauth2'","input":"apiKey","ctx":{"expected":"'oauth2'"},"url":"https://errors.pydantic.dev/2.11/v/literal_error"},{"type":"missing","loc":["securitySchemes","api-key","OpenIdConnectSecurityScheme","openIdConnectUrl"],"msg":"Field required","input":{"description":"the description of the security schema","in_":"header","name":"passed_api_key","type":"apiKey"},"url":"https://errors.pydantic.dev/2.11/v/missing"},{"type":"literal_error","loc":["securitySchemes","api-key","OpenIdConnectSecurityScheme","type"],"msg":"Input should be 'openIdConnect'","input":"apiKey","ctx":{"expected":"'openIdConnect'"},"url":"https://errors.pydantic.dev/2.11/v/literal_error"}]
Test 5: dict init with in
Error in server:
pydantic_core._pydantic_core.ValidationError: 7 validation errors for AgentCard
securitySchemes.api-key.APIKeySecurityScheme.in
Field required [type=missing, input_value={'description': 'the desc..._key', 'type': 'apiKey'}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.11/v/missing
securitySchemes.api-key.HTTPAuthSecurityScheme.scheme
Field required [type=missing, input_value={'description': 'the desc..._key', 'type': 'apiKey'}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.11/v/missing
securitySchemes.api-key.HTTPAuthSecurityScheme.type
Input should be 'http' [type=literal_error, input_value='apiKey', input_type=str]
For further information visit https://errors.pydantic.dev/2.11/v/literal_error
securitySchemes.api-key.OAuth2SecurityScheme.flows
Field required [type=missing, input_value={'description': 'the desc..._key', 'type': 'apiKey'}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.11/v/missing
securitySchemes.api-key.OAuth2SecurityScheme.type
Input should be 'oauth2' [type=literal_error, input_value='apiKey', input_type=str]
For further information visit https://errors.pydantic.dev/2.11/v/literal_error
securitySchemes.api-key.OpenIdConnectSecurityScheme.openIdConnectUrl
Field required [type=missing, input_value={'description': 'the desc..._key', 'type': 'apiKey'}, input_type=dict]
For further information visit https://errors.pydantic.dev/2.11/v/missing
securitySchemes.api-key.OpenIdConnectSecurityScheme.type
Input should be 'openIdConnect' [type=literal_error, input_value='apiKey', input_type=str]
For further information visit https://errors.pydantic.dev/2.11/v/literal_error
fernando-torres-blip-ai commentedon Jun 16, 2025
Also facing same issue, the field "_in" alias must be python compliant.