ModelRiver uses structured error responses to help you quickly identify and resolve issues. Errors follow different formats depending on which API you use.
OpenAI-compatible error format
When using the OpenAI compatibility layer, errors follow the standard OpenAI error envelope:
1{2 "error": {3 "message": "Workflow 'suport_bot' not found. Did you mean 'support_bot'? Names use letters, digits and underscores.",4 "type": "invalid_request_error",5 "param": "model",6 "code": "model_not_found",7 "modelriver": {8 "reason": "near_miss",9 "suggestions": ["support_bot"],10 "docs_url": "https://modelriver.com/docs/api/gateway-migration"11 }12 }13}Branch on code, not on message: messages can change. The modelriver object appears only on model_not_found errors (see Workflow not found).
Error codes
| HTTP | type | code | Meaning |
|---|---|---|---|
| 400 | invalid_request_error | invalid_request_error | Missing or invalid parameters, or a workflow sent to the wrong endpoint for its request type (param: "model") |
| 400 | invalid_request_error | model_not_supported | Workflow uses a backend pipeline (use async API) |
| 400 | invalid_request_error | streaming_not_supported | stream: true isn't available for this workflow or request |
| 401 | n/a | n/a | Invalid, expired or revoked ModelRiver API key. The body is {"error": "<string>"}, not the object above (see Authentication errors) |
| 401 | authentication_error | invalid_api_key | The provider rejected the provider credentials stored in your project |
| 402 | workflow_budget_exceeded | budget_exceeded | All workflow models are over their configured spend caps (scope: "workflow") |
| 402 | project_budget_exceeded | project_budget_exceeded | Project spend cap reached (scope: "project") |
| 402 | project_quota_exceeded | project_quota_exceeded | The project's monthly request quota is used up until period_end (see Usage API) |
| 403 | n/a | n/a | The project is archived. Plain-string body, like the 401 above |
| 404 | invalid_request_error | model_not_found | model isn't a callable workflow name in this project. See Workflow not found |
| 404 | invalid_request_error | not_found | Unknown path under /v1, POST /v1/responses (the Responses API isn't supported), or the compatibility layer is turned off |
| 422 | workflow_budget_config_error | budget_config_error | A capped workflow model cannot be evaluated because pricing or configuration is invalid |
| 422 | project_budget_config_error | project_budget_config_error | Project cap cannot be enforced for the routed model because pricing is unknown |
| 422 | invalid_request_error | model_unavailable | The configured model isn't available |
| 429 | rate_limit_exceeded | rate_limit_exceeded | Plan request limit reached: implement backoff or upgrade |
| 429 | rate_limit_error | rate_limit_exceeded | A workflow guardrail throttled the request; honour the retry-after header |
| 500 | server_error | internal_error | Internal failure |
| 502 | server_error | upstream_error | Provider returned an error |
Workflow budget errors report the specific type (workflow_budget_exceeded, workflow_budget_config_error) with the shorter legacy code (budget_exceeded, budget_config_error).
Native API error format
When using the native ModelRiver API (/v1/ai), errors are returned in the standard response envelope:
1{2 "status": "error",3 "message": "Provider request failed",4 "customer_data": {},5 "error": {6 "message": "Provider request failed",7 "details": {"status": 504, "message": "Upstream timeout"}8 },9 "meta": {10 "status": "error",11 "http_status": 502,12 "workflow": "marketing_summary",13 "attempts": [14 {"provider": "openai", "model": "gpt-4o-mini", "status": "error", "reason": "timeout"}15 ]16 }17}Note: ModelRiver returns
200with anerrorobject when the request was valid but the provider failed. Transport/authentication problems return standard HTTP status codes (401,403,429,5xx).
Common error scenarios
Authentication errors (401)
A missing, invalid, expired or revoked ModelRiver API key returns 401 with a plain string in error, on every API including the OpenAI-compatible endpoints:
1{2 "error": "Invalid or missing API key"3}Expired and revoked keys get the same message. An archived project returns 403 with {"error": "Project is archived"}. OpenAI SDKs raise these as AuthenticationError and PermissionDeniedError.
Solutions:
- Verify your API key starts with
mr_live_and hasn't expired - Check the key hasn't been rotated or revoked
- Ensure the
Authorizationheader usesBearerprefix - If you're switching from another gateway, make sure the old gateway key isn't still configured
See Authentication for the other authentication responses.
When the provider rejects the credentials stored in your project, the compatibility endpoints return an OpenAI-style 401 instead:
1{2 "error": {3 "message": "Invalid API key or provider configuration. Please check your provider credentials in the ModelRiver console.",4 "type": "authentication_error",5 "code": "invalid_api_key"6 }7}Workflow not found (404)
model must be the name of a workflow in the API key's project that has a Live version and accepts this endpoint's request type. When it doesn't match, the 404 says why and suggests up to three workflows:
1{2 "error": {3 "message": "'gpt-4o' is a provider model. ModelRiver's model field takes a workflow name. Workflow 'support_bot' in this project runs openai/gpt-4o — send model=\"support_bot\".",4 "type": "invalid_request_error",5 "param": "model",6 "code": "model_not_found",7 "modelriver": {8 "reason": "provider_model",9 "suggestions": ["support_bot"],10 "docs_url": "https://modelriver.com/docs/api/gateway-migration"11 }12 }13}modelriver.reason | Example message |
|---|---|
provider_model | 'gpt-4o' is a provider model. ModelRiver's model field takes a workflow name. Workflow 'support_bot' in this project runs openai/gpt-4o — send model="support_bot". |
provider_model (no matching workflow) | 'openai/gpt-4o' is a provider model. ModelRiver's model field takes a workflow name; the workflow picks provider, model and fallbacks. Create one in Console → Workflows. List yours: GET /v1/models |
ambiguous | 'my-wf' matches more than one workflow: 'my_wf', 'mywf'. Send the exact name. |
near_miss | Workflow 'suport_bot' not found. Did you mean 'support_bot'? Names use letters, digits and underscores. |
near_miss (case) | Workflow 'Support_Bot' not found. Names are case-sensitive — did you mean 'support_bot'? |
unknown | Workflow 'x' not found. This project has: a, b, c (+N more — GET /v1/models). |
none_callable | No workflow in this project accepts completion requests. Create a completion workflow in Console → Workflows. |
no_workflows | This project has no workflows yet. In ModelRiver, model is a workflow name, not a provider model. Create a workflow in Console → Workflows, then send its name. |
missing_live | Workflow 'support_bot' has no Live version. Publish it in Console → Workflows. |
Name matching: separators are normalized, case is not. my-workflow resolves to a workflow named my_workflow (or myworkflow), and the response then carries x-modelriver-model-resolved-from: my-workflow. My_Workflow does not match my_workflow. If one spelling matches two workflows, you get the ambiguous reason.
Native API (/v1/ai, /v1/ai/async): the workflow field (or model, when workflow is absent) uses the same name matching and the same reasons, in the native body shape. Messages name the field you sent, and any request type counts, including event-driven workflows. A separator-normalized match adds x-modelriver-workflow-resolved-from, and async jobs run under the stored workflow name.
1{2 "error": "Workflow not found",3 "code": "workflow_not_found",4 "message": "Workflow 'suport_bot' not found. Did you mean 'support_bot'? Names use letters, digits and underscores.",5 "reason": "near_miss",6 "suggestions": ["support_bot"],7 "docs_url": "https://modelriver.com/docs/api/gateway-migration"8}Solutions:
- Send a workflow name, not a provider model.
GET /v1/modelslists the names you can send - Match the name's case exactly
- Ensure the API key belongs to the project containing the workflow
- Publish the workflow if it has no Live version
- Coming from another gateway? See From other gateways
Backend pipeline workflow error (400)
1{2 "error": {3 "message": "Workflow 'my_workflow' uses a backend pipeline and cannot be used with the chat completions endpoint. Use the async API instead.",4 "type": "invalid_request_error",5 "param": "model",6 "code": "model_not_supported"7 }8}Solutions:
- Use the async endpoint (
POST /v1/ai/async) for backend pipeline workflows - Or remove the
event_namefrom the workflow configuration - See Webhooks for backend pipeline workflow details
Provider errors (502)
1{2 "error": {3 "message": "Provider request failed",4 "details": {"status": 504, "message": "Upstream timeout"}5 },6 "meta": {7 "attempts": [8 {"provider": "openai", "model": "gpt-4o", "status": "error", "reason": "timeout"},9 {"provider": "anthropic", "model": "claude-3-5-sonnet", "status": "error", "reason": "rate_limited"}10 ]11 }12}Solutions:
- Check provider status pages for outages
- Add fallback providers to your workflow
- Implement client-side retry with exponential backoff
- Review the
attemptsarray to understand which providers were tried
Workflow budget exceeded (402)
When every model in a workflow failover chain has reached its configured spend cap, ModelRiver returns a typed budget error instead of calling a provider. See Spending limits for setup and enforcement details.
1{2 "error": {3 "type": "workflow_budget_exceeded",4 "scope": "workflow",5 "message": "All workflow models are over budget",6 "workflow": "support_bot",7 "attempts": [8 {9 "provider": "openai",10 "model": "gpt-4o",11 "status": "skipped",12 "reason": {13 "message": "daily budget ($20.00) reached"14 }15 }16 ]17 }18}Solutions:
- Raise or remove the workflow spend cap in the console
- Wait for the UTC budget period to reset (
hourly,daily,weeklystarting Monday 00:00 UTC, ormonthly) - Add a lower-cost backup model with available budget
- Ensure capped models have current USD pricing in the model catalog
Console visibility: The console shows live spent / cap / remaining per workflow slot with 80% and 90% warning states. Spend totals may lag by up to ~10 seconds under load because enforcement uses a soft cached sum.
When a capped model cannot be evaluated because pricing is missing or invalid, ModelRiver returns a configuration error instead of HTTP 402:
1{2 "error": {3 "type": "workflow_budget_config_error",4 "scope": "workflow",5 "message": "Pricing is unknown, so this capped model was skipped",6 "workflow": "support_bot",7 "attempts": [8 {9 "provider": "openai",10 "model": "gpt-4o",11 "status": "skipped",12 "reason": {13 "message": "Pricing is unknown, so this capped model was skipped"14 }15 }16 ]17 }18}Solutions:
- Restore USD pricing for the model in the catalog
- Remove the spend cap until pricing is available
- Add a backup model with known pricing
Project budget exceeded (402)
When a project-level spend cap is configured and reached, ModelRiver blocks cache-miss provider requests before workflow failover. No backup models are attempted. See Spending limits.
1{2 "error": {3 "type": "project_budget_exceeded",4 "scope": "project",5 "message": "Project spend cap reached for this daily period",6 "cap_usd": "100.00",7 "spent_usd": "100.00",8 "period": "daily",9 "reset_at": "2026-07-04T00:00:00Z"10 }11}Solutions:
- Raise or remove the project cap in Project Settings
- Wait for the UTC budget period to reset
- Cache hits with zero recorded price are still allowed
Project budget config error (422)
When a project cap is configured but the routed model has unknown USD pricing, ModelRiver blocks the request even if current spend is below the cap.
1{2 "error": {3 "type": "project_budget_config_error",4 "scope": "project",5 "message": "Project spend cap cannot be enforced for this request because pricing is unknown for openai/gpt-4o. Add USD pricing or remove the project cap."6 }7}Rate limiting (429)
1{2 "error": {3 "message": "Rate limit exceeded. Please retry after 30 seconds.",4 "type": "rate_limit_error",5 "code": "rate_limit_exceeded"6 }7}Solutions:
- Implement exponential backoff
- Check your plan's rate limits
- Contact support to increase limits if needed
Retry strategies
Exponential backoff (Python)
1import time2import random3from openai import OpenAI, RateLimitError, APIStatusError4 5client = OpenAI(6 base_url="https://api.modelriver.com/v1",7 api_key="mr_live_YOUR_API_KEY"8)9 10def make_request_with_retry(messages, max_retries=3):11 for attempt in range(max_retries):12 try:13 return client.chat.completions.create(14 model="my_workflow",15 messages=messages16 )17 except RateLimitError:18 if attempt < max_retries - 1:19 wait = (2 ** attempt) + random.random()20 print(f"Rate limited. Retrying in {wait:.1f}s...")21 time.sleep(wait)22 else:23 raise24 except APIStatusError as e:25 if e.status_code >= 500 and attempt < max_retries - 1:26 wait = (2 ** attempt) + random.random()27 print(f"Server error. Retrying in {wait:.1f}s...")28 time.sleep(wait)29 else:30 raiseExponential backoff (Node.js)
1import OpenAI from "openai";2 3const client = new OpenAI({4 baseURL: "https://api.modelriver.com/v1",5 apiKey: "mr_live_YOUR_API_KEY",6});7 8async function makeRequestWithRetry(messages, maxRetries = 3) {9 for (let attempt = 0; attempt < maxRetries; attempt++) {10 try {11 return await client.chat.completions.create({12 model: "my_workflow",13 messages,14 });15 } catch (error) {16 const isRetryable =17 error.status === 429 || error.status >= 500;18 19 if (isRetryable && attempt < maxRetries - 1) {20 const wait = Math.pow(2, attempt) * 1000 + Math.random() * 1000;21 console.log(`Retrying in ${(wait / 1000).toFixed(1)}s...`);22 await new Promise((r) => setTimeout(r, wait));23 } else {24 throw error;25 }26 }27 }28}Error handling best practices
- Always check
meta.attempts: Understand which providers were tried and why they failed - Implement exponential backoff: Essential for
429and5xxerrors - Add fallback providers: Configure backup providers in workflow settings to reduce errors
- Log error details: Store
error.detailsandmeta.attemptsfor post-mortem analysis - Handle gracefully in UI: Show meaningful messages to users, not raw error payloads
- Set client-side timeouts: Don't wait indefinitely for responses
- Monitor error rates: Use Observability to track error patterns
- Differentiate error types: Authentication errors need different handling than transient provider errors
Next steps
- Authentication: API key management
- Observability: Monitor errors in Request Logs
- Troubleshooting: Common issues and solutions