Errors come from three places: the MCP transport, the broker tools, and the provider. A transport failure is the exception: an unauthenticated request gets a bare JSON-RPC error with a code and a message, and nothing else. The broker tools return a structured envelope with a stable code, a retryable flag and the side effects that did or did not happen. Read the code, not the prose.
OAuth and the connection
| Symptom | Cause | Fix |
|---|---|---|
| The browser did not open | The client could not launch a browser, or the session is headless | Copy the authorization URL the client printed and open it yourself |
| Signed in as the wrong GitHub account | The OAuth session decides the human, not the email you typed | Sign out of the client's session, sign out of GitHub, reconnect |
401 with a WWW-Authenticate header |
The bearer token is missing, expired or revoked | Re-run the client's OAuth step |
403 "Master Agent Delegation ToS has not been signed" |
The human has not signed the agreement | Sign it in onboarding, then reconnect |
403 "Human owner account is suspended" |
The human account is suspended | Contact the security address in the site footer |
| Tools disappear after a while | The client dropped the session or the token expired | Reconnect; the agent identity is resumed, not recreated |
The MCP endpoint returns 401 for an unauthenticated request with a JSON-RPC error and a WWW-Authenticate challenge. That is the expected first response, not a broken server.
Registration
| Symptom | Cause | Fix |
|---|---|---|
identity_register returns an agent you did not just create |
The session is bound to the agent it registered; that token-bound agent wins over name |
Expected. Start a new OAuth session and use a different name for a distinct identity |
agent_revoked |
The sub-identity is blocked | Reactivate it in the portal, or register a different name from a new session |
internal_error |
Broker or database failure | Retry; registration is idempotent |
Prove and record
| Code | Retryable | Cause | Fix |
|---|---|---|---|
human_email_unverified |
no | The human has not verified the email | Verify it in onboarding |
master_tos_unsigned |
no | The master agreement is unsigned | Sign it in onboarding |
human_suspended |
no | The human account is suspended | Contact the security address |
agent_revoked |
no | The kill switch is on | Reactivate, or use a new identity |
scope_not_granted |
no | requested_scopes includes something the agent does not hold |
Request only delegated scopes |
tos_text_hash_mismatch |
no | The text you sent does not hash to the hash you sent | Re-read terms.text, recompute the hash, send the exact bytes |
ledger_email_failed |
yes | The acceptance was recorded but the receipt did not send | Retry the same call; only the mail is retried |
signup_token_invalid |
no | The receipt failed verification | Get a valid token from the provider |
signup_token_not_linked |
no | The receipt references an acceptance this broker has not recorded | Run identity_prove for that audience, then retry |
signup_token_ownership_mismatch |
no | The receipt belongs to another human | Stop; use the token for your account |
internal_error |
yes | Broker failure | Retry with the same inputs; check side_effects.assertion_minted |
Two habits prevent most of these. Send the terms text exactly as received, with no trimming or re-wrapping. And retry the same call rather than a new one: an identical identity_prove cannot create a second acceptance.
Discovery
| Symptom | Cause | Fix |
|---|---|---|
404 or 410 on /.well-known/agent-signup.json |
The service does not support the protocol | Stop. This is not a retryable error |
The document parses but protocol is different |
Another protocol or a malformed document | Treat the service as unsupported |
issuer_allowlist does not contain your broker origin |
The provider does not accept this broker | Do not proceed; tell the human |
required_scopes is not covered by the agent's scopes |
The provider asks for more than was delegated | Ask the human to re-delegate; do not proceed |
The recomputed hash differs from terms.sha256 |
The document and the terms disagree | Stop. Re-fetch; if it still differs, report it to the provider |
| The document advertises SKUs, provisioning or quotas | Non-conformant vendor capability in discovery | Do not rely on those fields; report it to the provider |
Signup at the provider
The specification fixes one error set for relying parties. error_description is for humans and must not drive control flow; use error and, when present, retryable. A provider that returns a different code is not conformant; report it rather than guessing.
| Code | HTTP | Meaning | Action |
|---|---|---|---|
invalid_request |
400 | Malformed headers or body | Fix the request; do not retry blindly |
invalid_assertion |
401 | Signature or shape failed | Mint a new assertion |
issuer_not_allowed |
403 | iss is not in the allowlist |
Stop; this broker is not accepted |
audience_mismatch |
403 | aud does not equal audience |
Re-discover and mint for the right audience |
assertion_expired |
401 | exp passed |
Mint a new assertion |
assertion_replayed |
409 | The jti was used in a conflicting context |
Mint a new assertion; do not reuse |
tos_hash_mismatch |
409 | The accepted hash is stale | Re-discover, re-read, re-prove, then retry |
insufficient_scope |
403 | A required scope is missing | Ask the human to re-delegate |
human_unverified |
403 | human.verified is not true |
Verify the human email |
rate_limited |
429 | Throttled | Back off for the advertised delay |
A resolution: "resolved" response is a success, not an error. A provider that returns no credential on resolve is behaving correctly.
Inbox
| Symptom | Cause | Fix |
|---|---|---|
wait_timeout |
Nothing matched inside the lookback window or the budget | Raise since_seconds_ago, check the filters, or call inbox_list first |
| A message exists but the wait missed it | The filter is a substring of the raw address or subject | Match on subject_contains or a domain fragment that appears in the raw value |
message_not_found |
The id belongs to another agent, or no longer exists | List the inbox and use an id from the result |
detected_otp is null |
No standalone six-digit number in the subject or body | Read the body; the code may be split or formatted differently |
| Two forwarded receipts for one signup | Two acceptances exist, for example a changed terms hash or force_new |
Check the ledger; the fix is to stop calling force_new |
send_failed |
The outbound provider rejected the message | Nothing was sent; retry once, then investigate |
A lost credential
Do not re-run signup. A resolve returns no credential. Use the provider's rotation path, described in Credentials and recovery.
Where to get help
- Security issues in the broker, the protocol or these docs:
security@agentregistrar.dev. - Documentation and specification defects: the specification repository issues.
- Provider-side problems: the provider's own support and security contacts, which its discovery document or guide should name.