Skip to main content

Troubleshooting: A2A federation discovery and peer tasks

Four failure classes cover every A2A federation problem a tenant hits: the public AgentCard will not serve, an inbound task envelope is rejected before a skill ever runs, an outbound delegation fails at the peer, or a peer registration is refused because the URL you pasted is not a public FQDN. Each class has its own gate with its own fix — this runbook matches them. The federation surface itself (discovery modes, the two transports, the signing schemes) is documented in the A2A federation model concept page and the A2A federation feature page; the registered error codes sit in the error reference. This page is the fix path for each gate.

Symptom class 1 — Register/discovery refused (A2A_DISCOVERY_DISABLED)

The dashboard’s Agents → → A2A tab refuses to load the agent’s card with a 422 A2A_DISCOVERY_DISABLED, or a peer reports that the public /.well-known/agent-card.json URL returns 404 for a perfectly valid agent id. Both are the same gate: the agent’s a2a_discovery_mode is disabled (the default) or tenant. The default is deliberately closed: serving a card to the open internet would expose the agent’s skill catalog to every crawler, and the 404 on the public route is designed to leak nothing — it looks identical to “agent does not exist.” Fix for operators: open Agents → → A2A in the dashboard and set the agent’s discovery toggle to public (or tenant when you hand the share URL over an authenticated channel). The same toggle lives on PUT /agents/{agentId} as a2a_discovery_mode — see A2A federation → discovery modes.

Symptom class 2 — Inbound task envelope rejected on the signature (401)

A peer POSTs to /a2a or /a2a/tasks and gets a 401 with a fixed, operator-safe message. The exact discriminant (missing / malformed / expired / mismatched signature) is never leaked to the peer by design. The envelope must carry the Stripe/Svix-style header:
The most common causes, in the order to check them:
  1. Wrong secret — the peer signs with an old HMAC key (after a rotation) or a key minted for a different tenant.
  2. Signed the wrong bytes — the payload is ${ts}.${raw_body} exactly as sent: one extra newline or a re-serialized body whose wire bytes differ from the bytes you hashed fails the check.
  3. Stale timestamp — the signed t= must sit within the ±60-second replay window. A clock skew beyond 60 s on the peer’s infra fails as “mismatched,” not as “expired.”
  4. Missing header entirely — a Python requests / urllib client that set the header on one branch but not another behaves as unsigned.
Fix: whoever owns the signing key (you or your peer) rotates it, re-share it over a side channel, and re-sign. Until then every task retries the same gate; a busy 401 loop only burns your own logs. (For JSON-RPC peers: the 401 rides the JSON-RPC INVALID_REQUEST shape, but the cause list is identical.)

Symptom class 3 — Outbound task fails with A2A_PEER_ERROR (502)

Orbit forwards the task to the peer and the downstream agent errors it: 502 A2A_PEER_ERROR. The fix lives on the peer’s side, not yours. The envelope the peer returned carries the cause — read it before doing anything else:
  • Peer-side agent errored the task. Their run log (or, on another Orbit tenant, their dashboard A2A tab) holds the failing turn.
  • The peer’s AgentCard became unreachable. Re-fetch it: the cached catalog refreshed without a usable endpoint.
  • Your peer registry drifted. A URL that pointed at one agent now resolves to none — the peer row still looks healthy.
Do not retry in a loop. A peer that rejects on every request answers the same gate per retry, and hammering a failing endpoint earns the peer’s 429 rather than your task. Fix the peer, then dispatch once.

Symptom class 4 — Peer registration refused (public-FQDN check)

POST /a2a/peers returns 400 VALIDATION_ERROR (or the route’s SSRF gate) when the passed peer_url is a localhost address, a private IP/CGN range, a link-local cloud-metadata host, or any internal-only suffix. The guard runs on the URL itself, not the tenant’s posture — no tenant toggle can paste an on-network peer. Fix: publish the peer’s AgentCard at a public FQDN over HTTPS and re-register. A peer served from inside a VPC cannot federate across trust boundaries; place the public card behind a proxy or on a public staging URL.

Decision checklist

Run the checks in this order and stop at the first one that answers:
  1. Confirm the discovery toggle. Agents → {agent} → A2A: is the agent’s mode public (inbound-class problems) or is the peer in your registry at all (outbound-class problems)? Dashboard-fetch 422 and public-fetch 404 both answer “no.”
  2. Confirm the peer’s signing key. If the task is inbound-rejected, re-share the HMAC secret and verify the peer signs ${ts}.${raw_exact_bytes} within ±60 s.
  3. Read the peer-error envelope. Pull the id of the outbound task on GET /a2a/tasks (dashboard) or tasks/get (REST/JSON-RPC), and read the peer’s returned error.
  4. Inspect the agent-side run. Open the run in Live monitor — the same turn the peer’s engine executed shows where the failure actually sat.

What NOT to do

  • Do not leave discovery open on a dev tenant. A public toggle on a scratch tenant gives every crawler your agent’s skill catalog. Flip it off when you are done.
  • Do not try to bypass the signature check. Once a signing key is registered, every unsigned envelope must be rejected — the only fail-open boundary is “no key registered at all,” per the concept page. Do not ask the peer to send unsigned.
  • Do not re-task in a tight loop. Hammer a failing peer and you trade your retry for its 429. Use backoff and fix the peer first.

Full-error sample (paste-ready)

Copy this envelope into a ticket so support gets the stable anchors:
  • request_id — the id you key on
  • catalog_p1 — the route+agent+peer triple
  • direction — inbound vs outbound
For an inbound-signature failure, also include the X-A2A-Signature header’s t= timestamp so the ±60-second window can be checked.

When to escalate

Escalate to support only after the checklist empties. An inbound-side problem needs both tenants side-by-side — the agent owner’s discovery-mode state and the peer’s signed-envelope sample. For an outbound-side problem, include the peer’s task id plus your own request_id from meta; without both, one side can only say “the other tenant’s gate fired.”

See also