Skip to content

Agent Engine update() drops the AgentCard: spec.agent_card missing from the update mask #7064

Description

@msteiner-google

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

  1. Deploy an A2A agent with client.agent_engines.create(agent=reasoning_engines.A2aAgent(agent_card=card, ...), config=...), where card.version == "1.2.0".
  2. Edit the agent card: add a skill and bump version to "1.3.0".
  3. Redeploy onto the same engine with client.agent_engines.update(name=..., agent=a2a_agent, config=...).
  4. 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    api: vertex-aiIssues related to the googleapis/python-aiplatform API.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions