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:
- Wrong secret — the peer signs with an old HMAC key (after a rotation) or a key minted for a different tenant.
- 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. - 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.” - Missing header entirely — a Python
requests/urllibclient that set the header on one branch but not another behaves as unsigned.
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.
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:- Confirm the discovery toggle.
Agents → {agent} → A2A: is the agent’s modepublic(inbound-class problems) or is the peer in your registry at all (outbound-class problems)? Dashboard-fetch422and public-fetch404both answer “no.” - 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. - Read the peer-error envelope. Pull the
idof the outbound task onGET /a2a/tasks(dashboard) ortasks/get(REST/JSON-RPC), and read the peer’s returned error. - 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 oncatalog_p1— the route+agent+peer tripledirection— inbound vs outbound
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 ownrequest_id from meta; without both, one side can only say “the other
tenant’s gate fired.”
See also
- A2A federation → — endpoint-by-endpoint reference: discovery modes, the two transports, peer registry.
- A2A federation model → — the concept page this runbook assumes.
- Troubleshooting: agent runtime errors → — sibling runbook for the run-time failure codes a federated task can also route.
- Error reference — the full registered code catalog, A2A subsection.