AI generated, but the bug is personally verified
Environment details
- OS type and version: macOS 26.5.2 (arm64)
- Python version: 3.13.14
- pip version: n/a (uv 0.11.26)
google-cloud-aiplatform version: 1.156.0 (also present on main @ 4c0c368)
Steps to reproduce
- Deploy an A2A agent with
client.agent_engines.create(agent=reasoning_engines.A2aAgent(agent_card=card, ...), config=...), where card.version == "1.2.0".
- Edit the agent card: add a skill and bump
version to "1.3.0".
- Redeploy onto the same engine with
client.agent_engines.update(name=..., agent=a2a_agent, config=...).
- A new revision is created and
updateTime moves, but GET on the reasoning engine still returns spec.agentCard exactly as it was at step 1 — old version, old skill list. The Agent Registry entry derived from the card stays stale with it.
Code example
from a2a.types import AgentCard
from vertexai.preview import reasoning_engines
import vertexai
client = vertexai.Client(project=PROJECT, location=REGION)
card = AgentCard(**json.load(open("agent-card.json"))) # version "1.3.0", new skill added
a2a_agent = reasoning_engines.A2aAgent(agent_card=card, agent_executor_builder=build_executor)
updated = client.agent_engines.update(
name="projects/P/locations/L/reasoningEngines/E",
agent=a2a_agent,
config={"requirements": reqs, "extra_packages": ["agents"], "staging_bucket": BUCKET},
)
# spec.agentCard is still the card from the very first deployment.
print(updated.api_resource.spec.agent_card["version"]) # -> "1.2.0"
Analysis
In _create_config, the agent card is written into the request body but never added to update_masks, so the server drops it on a PATCH. Only spec.agent_framework is appended right after the card block:
|
if hasattr(agent, "agent_card"): |
|
agent_card = getattr(agent, "agent_card") |
|
if agent_card is not None: |
|
try: |
|
agent_engine_spec["agent_card"] = ( |
|
_agent_engines_utils._serialize_agent_card_to_dict( |
|
agent_card |
|
) |
|
) |
|
except Exception as e: |
|
raise ValueError( |
|
f"Failed to convert agent card to dict (serialization error): {e}" |
|
) from e |
|
update_masks.append("spec.agent_framework") |
if hasattr(agent, "agent_card"):
agent_card = getattr(agent, "agent_card")
if agent_card is not None:
try:
agent_engine_spec["agent_card"] = (
_agent_engines_utils._serialize_agent_card_to_dict(
agent_card
)
)
except Exception as e:
raise ValueError(
f"Failed to convert agent card to dict (serialization error): {e}"
) from e
update_masks.append("spec.agent_framework") # <- no "spec.agent_card"
Every other spec field that _create_config sets registers a matching mask entry (spec.package_spec.*, spec.class_methods, spec.container_spec, spec.deployment_spec.*, spec.identity_type, spec.service_account), so spec.agent_card looks like an oversight rather than an intentional omission. The API itself accepts the field: patching the engine directly with ?updateMask=spec.agentCard updates the card as expected, which is the workaround we are currently using.
Suggested fix — append the mask inside the if agent_card: branch:
agent_engine_spec["agent_card"] = ...
update_masks.append("spec.agent_card")
Impact: an A2A agent's advertised card (skills, version, description, security schemes) can never be changed after the first deployment through the SDK. Consumers that discover the agent through the card — other A2A clients and the Agent Registry — keep seeing capabilities the deployed revision no longer matches.
Related: #6183 is the same class of bug for python_version (set in the payload, missing from the update mask), so a sweep over _create_config for other unmasked spec fields may be worthwhile.
AI generated, but the bug is personally verified
Environment details
google-cloud-aiplatformversion: 1.156.0 (also present onmain@4c0c368)Steps to reproduce
client.agent_engines.create(agent=reasoning_engines.A2aAgent(agent_card=card, ...), config=...), wherecard.version == "1.2.0".versionto"1.3.0".client.agent_engines.update(name=..., agent=a2a_agent, config=...).updateTimemoves, butGETon the reasoning engine still returnsspec.agentCardexactly as it was at step 1 — old version, old skill list. The Agent Registry entry derived from the card stays stale with it.Code example
Analysis
In
_create_config, the agent card is written into the request body but never added toupdate_masks, so the server drops it on aPATCH. Onlyspec.agent_frameworkis appended right after the card block:python-aiplatform/vertexai/_genai/agent_engines.py
Lines 2499 to 2512 in 4c0c368
Every other spec field that
_create_configsets registers a matching mask entry (spec.package_spec.*,spec.class_methods,spec.container_spec,spec.deployment_spec.*,spec.identity_type,spec.service_account), sospec.agent_cardlooks like an oversight rather than an intentional omission. The API itself accepts the field: patching the engine directly with?updateMask=spec.agentCardupdates the card as expected, which is the workaround we are currently using.Suggested fix — append the mask inside the
if agent_card:branch:Impact: an A2A agent's advertised card (skills, version, description, security schemes) can never be changed after the first deployment through the SDK. Consumers that discover the agent through the card — other A2A clients and the Agent Registry — keep seeing capabilities the deployed revision no longer matches.
Related: #6183 is the same class of bug for
python_version(set in the payload, missing from the update mask), so a sweep over_create_configfor other unmasked spec fields may be worthwhile.