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 'xyz' does not exist. Create it in the console first.",
4 "type": "invalid_request_error",
5 "param": "model",
6 "code": "model_not_found"
7 }
8}

Error codes

HTTPCodeMeaning
400invalid_request_errorMissing or invalid parameters
401authentication_errorInvalid or missing API key
404model_not_foundWorkflow doesn't exist
400model_not_supportedWorkflow is event-driven (use async API)
429rate_limit_exceededToo many requests: implement backoff
402budget_exceededLegacy alias for workflow spend caps reached (prefer workflow_budget_exceeded)
402workflow_budget_exceededAll workflow models are over their configured spend caps (scope: "workflow")
402project_budget_exceededProject spend cap reached (scope: "project")
422budget_config_errorLegacy alias for workflow budget config issues (prefer workflow_budget_config_error)
422workflow_budget_config_errorA capped workflow model cannot be evaluated because pricing or configuration is invalid
422project_budget_config_errorProject cap cannot be enforced for the routed model because pricing is unknown
502upstream_errorProvider returned an error
503service_unavailableFeature disabled or internal failure

Native API error format

When using the native ModelRiver API (/v1/ai), errors are returned in wrapped format:

JSON
1{
2 "data": null,
3 "customer_data": {},
4 "error": {
5 "message": "Provider request failed",
6 "details": {"status": 504, "message": "Upstream timeout"}
7 },
8 "meta": {
9 "status": "error",
10 "http_status": 502,
11 "workflow": "marketing-summary",
12 "attempts": [
13 {"provider": "openai", "model": "gpt-4o-mini", "status": "error", "reason": "timeout"}
14 ]
15 }
16}

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)

JSON
1{
2 "error": {
3 "message": "Invalid API key provided",
4 "type": "authentication_error",
5 "code": "invalid_api_key"
6 }
7}

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

Workflow not found (404)

JSON
1{
2 "error": {
3 "message": "Workflow 'my-workflow' does not exist. Create it in the console first.",
4 "type": "invalid_request_error",
5 "param": "model",
6 "code": "model_not_found"
7 }
8}

Solutions:

  • Verify the workflow name matches exactly (case-sensitive)
  • Check the workflow exists in the correct project
  • Ensure the API key belongs to the project containing the workflow

Event-driven workflow error (400)

JSON
1{
2 "error": {
3 "message": "Workflow 'my-workflow' is event-driven and cannot be used with the chat completions endpoint. Use the async API instead.",
4 "type": "invalid_request_error",
5 "code": "model_not_supported"
6 }
7}

Solutions:

  • Use the async endpoint (POST /v1/ai/async) for event-driven workflows
  • Or remove the event_name from the workflow configuration
  • See Webhooks for event-driven 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