Documentation
Documentation
Error Codes
Error codes from every XAA endpoint, with causes and fixes.
Each section covers one endpoint. The Cause column is the specific reason the server rejected the request. Multiple causes can share the same error code.
IDP: Token Exchange
Endpoint: POST https://idp.xaa.dev/token with grant_type=urn:ietf:params:oauth:grant-type:token-exchange
| HTTP | Error Code | Cause | Fix |
|---|---|---|---|
| 400 | invalid_request | A required parameter is missing (audience, resource, subject_token, subject_token_type, or requested_token_type) | Include all required parameters. See Step 2 for the complete list. |
| 400 | invalid_request | subject_token_type is not urn:ietf:params:oauth:token-type:id_token | Use the correct token type; only ID Tokens are accepted |
| 400 | invalid_request | ID Token signature verification failed | The ID Token must be a valid JWT issued by this IDP; do not modify or hand-craft tokens |
| 400 | invalid_request | ID Token aud doesn't match the authenticated client_id | Your client must present an ID Token that was issued to it. The aud claim must equal your client_id. |
| 400 | invalid_request | ID Token has no sub claim | The ID Token must contain a subject identifier; re-authenticate |
| 400 | invalid_target | Client has resource connections but none match the requested audience + resource combination | Re-register the resource using the wizard. The client's resource connections must include this auth server and API URL pair. |
| 400 | invalid_scope | One or more requested scopes are not permitted for this resource connection | Remove the invalid scopes, or re-register the resource and add the missing scopes in Wizard Step 3 |
| 500 | server_error | Internal server error during token generation | Check server logs; retry once |
Auth Server: JWT Bearer Grant
Endpoint: POST https://auth.resource.xaa.dev/token with grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer
| HTTP | Error Code | Cause | Fix |
|---|---|---|---|
| 400 | invalid_grant | ID-JAG typ header is not oauth-id-jag+jwt | Only use ID-JAGs from the IDP's token exchange; do not hand-craft the assertion |
| 400 | invalid_grant | ID-JAG iss is not a trusted issuer | The IDP URL must be configured as a trusted provider; check that you're using the correct IDP |
| 400 | invalid_grant | Signature verification failed | The ID-JAG has been tampered with or was signed by a different key |
| 400 | invalid_grant | ID-JAG iat is more than 30 seconds in the future | Check server clock synchronization; the IDP and Auth Server clocks are more than 30 seconds apart |
| 400 | invalid_grant | ID-JAG has expired (exp in the past) | ID-JAGs expire in 5 minutes. Get a fresh one from Step 2 and retry immediately. |
| 400 | invalid_grant | ID-JAG client_id doesn't match the authenticating client | Use your resource client credentials (client_id-at-resource), not your main client credentials |
| 400 | invalid_grant | ID-JAG aud doesn't match this Auth Server's URL | In Step 2, set audience to this server's URL exactly. Check for trailing slash discrepancies. |
| 401 | invalid_client | Client authentication failed (wrong credentials) | Verify your resource client ID and secret; re-register via Client Registration if needed |
| 400 | unauthorized_client | Client is not registered at the Auth Server | Register via Client Registration with a resource connection |
If the requested scope is not a subset of the ID-JAG's scope, the Auth Server does not reject the request. It issues a token with the intersection. An empty intersection produces a token with no scope, which will fail at the resource server with 403 insufficient_scope.
See Token Structure → Scope intersection rule for details.
Resource Server
Endpoint: GET https://api.resource.xaa.dev/api/todos (and other protected routes)
| HTTP | Error Code | Cause | Fix |
|---|---|---|---|
| 401 | unauthorized | No Authorization header | Add Authorization: Bearer <access_token> to every request |
| 401 | invalid_token | Token expired, signature invalid, wrong issuer, wrong algorithm, or aud does not match the URL registered in Wizard Step 1 | Get a new access token from Step 3; verify your token against https://auth.resource.xaa.dev/jwks using RS256; set your middleware's expected audience to the exact URL you registered (trailing slash must match) |
| 403 | insufficient_scope | Token doesn't include the required scope | Ensure the scope was requested in Step 2 and granted in Step 3. Check the scope claim in your access token. |
Per RFC 6750, resource servers should return a WWW-Authenticate: Bearer challenge on 401/403 so clients can distinguish the failure mode:
MCP Server
Endpoint: POST https://mcp.xaa.dev/mcp
| HTTP | JSON-RPC Error | Cause | Fix |
|---|---|---|---|
| 401 | Unauthorized: Invalid or expired access token | Missing, expired, wrong-audience, or invalid Bearer token | Same as Resource Server invalid_token above. The aud must match the resource URL you registered in Wizard Step 1 (the MCP URL is not the audience — the resource URL is). |
| 403 | Insufficient scopes | Token lacks todos.read or mcp.access | Request both scopes in Step 2: scope: 'todos.read mcp.access' |
| 406 | (none) | Accept header missing or doesn't include text/event-stream | Add Accept: application/json, text/event-stream to all requests |
MCP servers implementing RFC 9728 should include a resource_metadata hint on 401 so agents can auto-discover the Auth Server:
Common debugging checklist
If you're stuck and unsure which error applies:
- Decode your token: use the Token Decoder tab in the Demo's detail panel to inspect
iss,aud,scope, andexp - Check expiry: ID-JAG: 5 min, Access Token: 2 hours
- Check
aud: must be the exact URL of the service that consumes the token (no trailing slash mismatch) - Check
scope: remember the intersection rule - Check clock skew: if
iaterrors appear, synchronize your server's system clock
Related pages
- Token Structure: what each token contains and why
- Step 2: Token Exchange: required parameters
- Step 3: JWT Bearer Grant: required parameters
- Troubleshooting: UI-specific errors in the tester
On this page