## Transport errors

| Code                    | Status | Description                                                  | Recovery                                                                                     |
|-------------------------|--------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| 307_redirect            | 307    | Request hit /mcp without trailing slash.                    | Always call /mcp/ — many Streamable HTTP clients drop the connection on the redirect.      |
| 400_https_required      | 400    | Production environment received a non-HTTPS request.        | Set ENVIRONMENT=production only behind an HTTPS proxy; otherwise unset ENFORCE_HTTPS.     |
| spawn_enoent           | —      | Inspector configured as STDIO with an HTTP URL as command.  | Switch transport to Streamable HTTP. Use the URL field, not Command.                       |

## Authentication

| Code                    | Status | Description                                                  | Recovery                                                                                     |
|-------------------------|--------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| missing_bearer          | 401    | Authorization header missing or malformed.                  | Include `Authorization: Bearer <jwt>` on every request.                                    |
| invalid_audience        | 401    | Token `aud` != wmstudio-mcp.                               | Re-mint via /oauth/token or `create_test_token()` in development.                        |
| revoked_token           | 401    | Token present in Redis `mcp:tokens:revoked`.               | Restart the OAuth flow.                                                                     |
| bad_oauth_state        | —      | Supabase rejected a stale or duplicated state parameter.    | Most often a redirect_to misconfiguration — check the runbook in `oauth-fly-supabase-runbook.md`.|

## Input validation

| Code                    | Status | Description                                                  | Recovery                                                                                     |
|-------------------------|--------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| missing_required         | 400    | Required parameter not supplied (e.g. `prompt` or `image_url`).| Check the tool's `Parameters` table; required params are tagged.                            |
| asset_url_required      | —      | A tool that requires a public image/video URL was called without one. | Response includes `uploadUrl` pointing at the drag-and-drop page. Share the `uploadUrl` with the user; once they upload, the page returns a CDN URL ready to paste back into the tool. |
| asset_url_required (reason: unreachable)| — | URL provided but failed the HEAD preflight (DNS failure, 404, or non-2xx). Often a fabricated URL. | Never invent URLs. Always use the `uploadUrl` flow to obtain a real CDN-hosted asset.     |
| invalid_image           | 400    | Image URL is reachable but not a valid image, or larger than 12MB. | Re-host via the upload page; PNG/JPG/WebP only; max 12MB.                                   |
| unsupported_model       | 400    | Model id not enabled on this deployment.                    | See `/docs/configuration#models` for the live catalogue.                                   |
| consent_required        | 403    | Likeness or Digital Twin call missing `consent_acknowledged: true`. | Surface a consent UI in your client and set the flag explicitly.                            |

## Runtime

| Code                    | Status | Description                                                  | Recovery                                                                                     |
|-------------------------|--------|--------------------------------------------------------------|---------------------------------------------------------------------------------------------|
| upgrade_required        | 402    | Insufficient credits for this generation.                   | Response shape: `{ error: "upgrade_required", reason: "insufficient_credits", upgradeUrl, requiredCredits, availableCredits, message }`. Render `upgradeUrl` as a link to /dashboard/credits and **stop calling generation tools** until the user tops up. |
| insufficient_credits    | 402    | Legacy error code for the same condition as `upgrade_required` (kept for client compatibility).| Direct user to /dashboard/credits to top up.                                               |
| rate_limited            | 429    | Per-user or per-tool rate window exceeded.                  | Respect the `Retry-After` header; back off exponentially.                                   |
| provider_unavailable    | 503    | Upstream model provider is temporarily down (fal.ai, OpenAI, etc.).| Retry after 30–60s. The provider is reported in the error body.                             |
| job_timeout             | —      | Async job did not finish within its TTL (default 10 min).   | Retry with a smaller batch or simpler prompt.                                               |
| job_failed              | —      | Provider returned a render failure.                          | Inspect `error.message` for the provider's reason.                                         |

## By tool

Each tool documents its own error shapes — click through for the full table.

Tool-specific error tables are added as edge cases are catalogued.
