Documentation

Handle errors gracefully

Standard error envelopes, descriptive error codes, and production-ready retry strategies. Build resilient AI applications.

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:

JSON
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

HTTPtypecodeMeaning
400invalid_request_errorinvalid_request_errorMissing or invalid parameters, or a workflow sent to the wrong endpoint for its request type (param: "model")
400invalid_request_errormodel_not_supportedWorkflow uses a backend pipeline (use async API)
400invalid_request_errorstreaming_not_supportedstream: true isn't available for this workflow or request
401n/an/aInvalid, expired or revoked ModelRiver API key. The body is {"error": "<string>"}, not the object above (see Authentication errors)
401authentication_errorinvalid_api_keyThe provider rejected the provider credentials stored in your project
402workflow_budget_exceededbudget_exceededAll workflow models are over their configured spend caps (scope: "workflow")
402project_budget_exceededproject_budget_exceededProject spend cap reached (scope: "project")
402project_quota_exceededproject_quota_exceededThe project's monthly request quota is used up until period_end (see Usage API)
403n/an/aThe project is archived. Plain-string body, like the 401 above
404invalid_request_errormodel_not_foundmodel isn't a callable workflow name in this project. See Workflow not found
404invalid_request_errornot_foundUnknown path under /v1, POST /v1/responses (the Responses API isn't supported), or the compatibility layer is turned off
422workflow_budget_config_errorbudget_config_errorA capped workflow model cannot be evaluated because pricing or configuration is invalid
422project_budget_config_errorproject_budget_config_errorProject cap cannot be enforced for the routed model because pricing is unknown
422invalid_request_errormodel_unavailableThe configured model isn't available
429rate_limit_exceededrate_limit_exceededPlan request limit reached: implement backoff or upgrade
429rate_limit_errorrate_limit_exceededA workflow guardrail throttled the request; honour the retry-after header
500server_errorinternal_errorInternal failure
502server_errorupstream_errorProvider 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:

JSON
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 200 with an error object 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:

JSON
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 Authorization header uses Bearer prefix
  • 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:

JSON
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:

JSON
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.reasonExample 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_missWorkflow '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'?
unknownWorkflow 'x' not found. This project has: a, b, c (+N more — GET /v1/models).
none_callableNo workflow in this project accepts completion requests. Create a completion workflow in Console → Workflows.
no_workflowsThis 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_liveWorkflow '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.

JSON
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/models lists 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)

JSON
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_name from the workflow configuration
  • See Webhooks for backend pipeline workflow details

Provider errors (502)

JSON
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 attempts array 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.

JSON
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, weekly starting Monday 00:00 UTC, or monthly)
  • 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:

JSON
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.

JSON
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.

JSON
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)

JSON
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)

PYTHON
1import time
2import random
3from openai import OpenAI, RateLimitError, APIStatusError
4 
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=messages
16 )
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 raise
24 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 raise

Exponential backoff (Node.js)

JAVASCRIPT
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

  1. Always check meta.attempts: Understand which providers were tried and why they failed
  2. Implement exponential backoff: Essential for 429 and 5xx errors
  3. Add fallback providers: Configure backup providers in workflow settings to reduce errors
  4. Log error details: Store error.details and meta.attempts for post-mortem analysis
  5. Handle gracefully in UI: Show meaningful messages to users, not raw error payloads
  6. Set client-side timeouts: Don't wait indefinitely for responses
  7. Monitor error rates: Use Observability to track error patterns
  8. Differentiate error types: Authentication errors need different handling than transient provider errors

Next steps