Failures arrive in two shapes, and confusing them wastes time.
| Shape | Meaning | Where it is decided |
|---|---|---|
HTTP 4xx with no JSON-RPC result | The call never reached a tool | Method allowlist or credential, gates 1 and 2 |
HTTP 200 with "isError": true | The call reached the tool; the operation failed | Path policy or provider, gates 3 and 4 |
A 200 is not success. Check result.isError on every tools/call.
Transport failures
401 on /mcp
HTTP/1.1 401 Unauthorized
www-authenticate: Bearer resource_metadata="https://<host>/.well-known/oauth-protected-resource"
{"error":"Unauthorized"}The body is identical for every cause. Three produce it:
| Cause | How to tell | Fix |
|---|---|---|
| Anonymous call to a write tool | You sent no Authorization header | Follow the challenge: Authorize a client |
| Method not on the anonymous allowlist | The method is not initialize / tools/list / resources/list / prompts/list / an allowed tools/call | Authenticate, or use an allowed method |
| Credential rejected | You sent a header but it is malformed, unknown, or issued by a different blocklet | Obtain a credential from this host. Credentials are instance-bound |
An unknown tool name also returns 401 when you are anonymous, not a "no such tool" error. The allowlist is checked before the tool is looked up. If a tool name you saw in tools/list returns 401, it is a privileged tool, not a typo.
Authorization-flow failures
All are 400 with an RFC 6749 error code.
| Request | Response |
|---|---|
POST /oauth/register without redirect_uris | {"error":"invalid_redirect_uri","error_description":"Missing redirect_uris"} |
POST /oauth/register with an unsupported scheme | {"error":"invalid_redirect_uri","error_description":"redirect_uris must be https, RFC 8252 loopback http, or a private-use scheme …"} |
POST /oauth/token with an unknown or expired device_code | {"error":"invalid_grant","error_description":"Invalid or expired device_code"} |
POST /oauth/token with any other grant | {"error":"unsupported_grant_type","error_description":"Only device_code, authorization_code, and refresh_token grants are supported"} |
Two more you should expect but that are not shown above:
- Registration is rate limited per source address. A client that re-registers on every start will eventually be throttled. Register once and keep the
client_id. - A registration that is never used in a completed authorization is swept after seven days. A long-lived client that registered but never got consent must register again.
invalid_grant on a device code most often means the five-minute window expired, not that the code was wrong. Restart the flow.
In-call failures
These arrive as 200 with isError. The text is a code followed by the path and an explanation.
AFS_FORBIDDEN
AFS_FORBIDDEN: Forbidden at /instance/notes.txt: network clients cannot write base paths;
use a resolver overlay or internal codeThe blocklet has not opened that path to network clients. Variants:
| Operation | Text |
|---|---|
| write | cannot write base paths; use a resolver overlay or internal code |
| read | cannot read base paths; declare a networkRead rule or use an overlay |
| list | cannot list base paths; declare a networkRead rule or use an overlay |
| stat | cannot stat base paths; declare a networkRead rule or use an overlay |
A credential does not fix this. An owner-role caller gets the same refusal. Only the blocklet's own declaration changes it. See Access tiers, gate 3.
AFS_NOT_FOUND
AFS_NOT_FOUND: Path not found: /newThe path does not exist on this host. Paths are host-scoped, so a path that resolves on one blocklet can be absent on another even when both mount the same provider name. Confirm with afs_list on the parent before assuming the provider is missing.
Diagnosing in order
Work down the gates; each step rules out everything above it.
- Does anonymous
tools/listreturn200? No → the host is not reachable or is not an ARC blocklet. Check the URL and/.well-known/mcp.json. - Does the tool appear in
tools/list? No → for content tools, the blocklet declared no collections; see Declare what agents see. - Does calling it return
401? Yes → gate 1 or 2. Follow theWWW-Authenticatechallenge. - Does it return
200withAFS_FORBIDDEN? Yes → gate 3. The credential is fine; the path is closed to the network. - Does
afs_statshow the operation incapabilities? No → gate 4. The provider does not implement it.
Refreshing a credential
The device grant returns a credential with no expiry and no refresh token, so there is nothing to refresh. If a credential stops working, sign in again with your tool, see Connect your tool.