One envelope
{ error: { code, message, retry } }Four retry answersretry · change-input · needs-human · no
MCP is differentJSON-RPC errors ride HTTP 200
Errors
retry hint is the point: change-input means fix the request, needs-human means stop and ask, retry means try again, no means report it.| code | status | retry | when | what the agent says |
|---|---|---|---|---|
| not_found | 404 | change-input | The requested resource or capability does not exist. | "There is no app called hello here; did you mean hello-web?" |
| invalid_input | 422 | change-input | The request input failed schema validation. | "I need one more thing — every how often, or a cron?" |
| policy_blocked | 403 | needs-human | A governance Policy blocked the operation. | "This needs Docker on the control plane and it is not available." |
| permission_denied | 403 | needs-human | The Actor is not authorized to use this Capability or Resource. | "I am not allowed to do that for this account." |
| dependency_unavailable | 503 | needs-human | A required dependency (Docker, secret, provider) is unavailable. | "The platform's store is unreachable; I will not retry on my own." |
| internal_error | 500 | no | An unexpected platform error occurred. | "Something failed inside forge; here is the resource id to report." |
| rate_limited | 429 | — | per-IP limits on login, signup, /mcp, /oauth/* | “Too many attempts — I will wait 30 seconds.” |
| signature_invalid · webhook_retry | 400 · 503 | no · retry | Stripe webhook signature failed · transient webhook failure | — |
MCP: JSON-RPC errors on HTTP 200
| code | meaning |
|---|---|
| -32600 · -32601 · -32602 · -32603 | invalid request · method not found · missing/unknown tool · internal |
| -32001 | insufficient_scope with data.required_scope — forge-specific |
| -32011 | the app's handler is unreachable — forge-specific |
| (none) | a handler non-2xx becomes isError: true in the result, classed handler_status_<code> |
A rejected bearer on /mcp is always 401 invalid_token on the wire, deliberately; the real reason lives only on the mcp.auth_reject log line.