google-gemini/gemini-cli · error

Agent card is missing.

Error message

Agent card is missing.

What it means

normalizeAgentCard rejects its input when isObject(card) is false, meaning the value passed in is null, undefined, a primitive, or an array. The function is called in A2AClientManager.loadAgent after either JSON.parse(options.json) or DefaultAgentCardResolver.resolve(url). An empty/null card means the agent-card source produced nothing usable, so normalization cannot proceed.

Source

Thrown at packages/core/src/agents/a2aUtils.ts:260

    }
    if ('uri' in fileData && fileData.uri) {
      return `File: ${fileData.uri}`;
    }
    return `File: [binary/unnamed]`;
  }

  return '';
}

/**
 * Normalizes proto field name aliases that the SDK doesn't handle yet.
 * The A2A proto spec uses `supported_interfaces` and `protocol_binding`,
 * while the SDK expects `additionalInterfaces` and `transport`.
 * TODO: Remove once @a2a-js/sdk handles these aliases natively.
 */
export function normalizeAgentCard(card: unknown): AgentCard {
  if (!isObject(card)) {
    throw new Error('Agent card is missing.');
  }

  // Shallow-copy to avoid mutating the SDK's cached object.
  // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
  const result = { ...card } as unknown as AgentCard;

  // Map supportedInterfaces → additionalInterfaces if needed
  if (!result.additionalInterfaces) {
    const raw = card;
    if (Array.isArray(raw['supportedInterfaces'])) {
      // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
      result.additionalInterfaces = raw[
        'supportedInterfaces'
      ] as AgentInterface[];
    }
  }

  // Map protocolBinding → transport on each interface

View on GitHub (pinned to 5024443c72)

Solutions

  1. If using type 'json', validate the parsed value is a non-null object before loading.
  2. If using type 'url', curl the agent_card_url and confirm it returns JSON with an 'name'/'version' object.
  3. Check for corporate proxies or auth walls replacing the card with HTML.
  4. Ensure the remote A2A server is running and serving /.well-known/agent-card.json (or its configured path).

Example fix

// before
await manager.loadAgent('svc', { type: 'json', json: rawJson });

// after
const parsed = JSON.parse(rawJson);
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
  throw new Error('agent_card_json must decode to a single object');
}
await manager.loadAgent('svc', { type: 'json', json: rawJson });
Defensive patterns

Strategy: type-guard

Validate before calling

function isPlainObject(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v);
}

let card: unknown;
if (options.type === 'json') card = JSON.parse(options.json);
else card = await resolver.resolve(options.url, '');
if (!isPlainObject(card)) {
  throw new Error('Agent card must decode to a non-null object.');
}
await manager.loadAgent(name, options);

Type guard

function isAgentCardLike(v: unknown): v is Record<string, unknown> {
  return typeof v === 'object' && v !== null && !Array.isArray(v)
    && typeof (v as { name?: unknown }).name === 'string';
}

Prevention

When it happens

Trigger: An inline agent_card_json that parses to null (e.g. the string 'null'); a remote card endpoint returning an empty 200 body that the resolver decoded to undefined; a malformed JSON literal that JSON.parse turned into a number/string; the resolver returned null because the endpoint served HTML or a redirect page.

Common situations: Wrong agent_card_url pointing at an HTML error page or login wall; agent_card_json set to an empty object string but actually null; a proxy rewriting the response to a placeholder; the A2A server not yet started so the endpoint returns a gateway page.

Related errors


AI-assisted analysis of google-gemini/gemini-cli@5024443c72 (2026-08-12). Data as JSON: /api/errors/06299890b5b517ef. Report an issue: GitHub.