Skip to content

Error Handling

TokenRouter uses standard HTTP status codes and a consistent error response format across all endpoints. This guide covers all error types, their meanings, and how to handle them properly.

All errors follow a consistent JSON structure:

{
"error": {
"type": "string", // Error type identifier
"message": "string", // Human-readable error message
"code": "string|null", // Provider-specific error code (if applicable)
"provider": "string", // Provider name (e.g., "openai", "anthropic")
"http_status": integer, // HTTP status code
"raw": "object|null" // Raw provider response (debug mode only)
}
}

TokenRouter uses the following HTTP status codes:

Status CodeMeaningCommon Causes
200SuccessRequest completed successfully
201CreatedResource created (API keys, webhooks)
400Bad RequestMalformed request, invalid parameters
401UnauthorizedMissing or invalid API key, expired token
403ForbiddenInsufficient permissions, free plan limitation
404Not FoundResource doesn’t exist or not accessible
422Unprocessable EntityValidation failed, firewall blocked, routing error
429Too Many RequestsRate limit exceeded
500Internal Server ErrorUnexpected server error
503Service UnavailableProvider unavailable, maintenance mode

Returned when API key is missing, invalid, or expired.

{
"error": {
"type": "unauthorized_error",
"message": "Invalid API key",
"http_status": 401
}
}

Common Causes:

  • Missing Authorization header
  • Invalid API key format
  • Expired API key
  • Revoked API key

Solutions:

  • Verify API key is correct
  • Check API key hasn’t expired
  • Generate a new API key from dashboard
  • Ensure proper Bearer token format

Returned when attempting to access features not available on your plan.

{
"error": {
"type": "authorization_error",
"message": "Routing rules are not available on the free plan",
"http_status": 403
}
}

Common Causes:

  • Free plan accessing paid features (routing rules, firewall rules)
  • Insufficient user permissions
  • Resource belongs to different user

Solutions:

  • Upgrade to a paid plan
  • Verify plan includes requested feature
  • Check resource ownership

Returned when request data fails validation.

{
"error": {
"type": "validation_error",
"message": "Validation failed",
"http_status": 422,
"errors": {
"priority": ["The priority must be between -1000 and 1000"],
"match_json": ["The match_json field is required"]
}
}
}

Common Causes:

  • Missing required fields
  • Invalid field values
  • Out-of-range values
  • Invalid data types

Solutions:

  • Review field requirements
  • Validate data before sending
  • Check value constraints
  • Ensure proper data types

Returned when a request is blocked by a firewall rule.

{
"error": {
"type": "firewall_rule",
"message": "Request blocked by firewall rule \"Block PII\"",
"http_status": 422,
"meta": {
"rule_id": 123,
"rule_name": "Block PII",
"action": "block",
"reason": "Request blocked by firewall rule"
}
}
}

Common Causes:

  • Input contains sensitive data (SSN, credit cards, etc.)
  • Input matches firewall regex pattern
  • Output matches firewall pattern (response blocking)

Solutions:

  • Remove sensitive data from input
  • Use mask action instead of block
  • Adjust firewall rule patterns
  • Disable rule if not needed

Returned when you exceed rate limits.

{
"error": {
"type": "rate_limit_error",
"message": "Rate limit exceeded. Please try again in 30 seconds",
"http_status": 429,
"retry_after": 30
}
}

Rate Limit Types:

  • Requests per minute (RPM) - Number of requests
  • Tokens per minute (TPM) - Token usage
  • Daily quota - Total tokens per day
  • Monthly quota - Total tokens per month

Solutions:

  • Implement exponential backoff
  • Use retry_after header value
  • Reduce request frequency
  • Upgrade plan for higher limits
  • Monitor usage via dashboard

Returned when the underlying LLM provider encounters an error.

{
"error": {
"type": "provider_error",
"message": "The model is currently overloaded. Please try again",
"code": "model_overloaded",
"provider": "openai",
"http_status": 503
}
}

Common Provider Error Codes:

  • model_overloaded - Provider infrastructure overloaded
  • server_error - Internal provider error
  • timeout - Request timed out
  • invalid_api_key - Provider API key invalid or expired

Solutions:

  • Implement retry logic with exponential backoff
  • Use auto-routing for automatic fallback
  • Switch to different provider manually
  • Check provider status page

Returned when routing logic encounters an issue.

{
"error": {
"type": "routing_error",
"message": "No suitable provider available for requested model",
"http_status": 422,
"meta": {
"requested_model": "gpt-4o",
"available_providers": []
}
}
}

Common Causes:

  • No provider keys configured
  • All providers unavailable
  • Model not supported by any provider
  • All routing rules failed to match

Solutions:

  • Add provider keys in dashboard
  • Use auto-routing for flexibility
  • Check model availability
  • Review routing rule configuration

Errors during streaming are delivered as SSE events:

event: error
data: {"type":"provider_error","message":"Rate limit exceeded","http_status":429}
event: done
data: null

Implement exponential backoff for transient errors:

async function makeRequestWithBackoff(
maxRetries = 3,
baseDelay = 1000
) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await client.responses.create({
model: 'gpt-4o',
input: 'Your prompt'
});
} catch (error: any) {
const isRetryable = [429, 500, 502, 503, 504].includes(error.status);
const isLastAttempt = attempt === maxRetries - 1;
if (!isRetryable || isLastAttempt) {
throw error;
}
const delay = baseDelay * Math.pow(2, attempt);
console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}

Fallback to simpler models or cached responses:

async function makeRequestWithDegradation(input: string) {
const modelFallbacks = [
'gpt-4o', // Try premium model first
'gpt-4o-mini', // Fall back to mini
'auto:cost' // Use cheapest available
];
for (const model of modelFallbacks) {
try {
return await client.responses.create({ model, input });
} catch (error: any) {
console.warn(`${model} failed:`, error.message);
// Try next model
}
}
// All models failed - return cached or default response
throw new Error('All models unavailable');
}

Prevent cascading failures with a circuit breaker pattern:

class CircuitBreaker {
private failures = 0;
private lastFailure = 0;
private readonly threshold = 5;
private readonly timeout = 60000; // 60 seconds
async call<T>(fn: () => Promise<T>): Promise<T> {
if (this.isOpen()) {
throw new Error('Circuit breaker is open');
}
try {
const result = await fn();
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private isOpen(): boolean {
if (this.failures >= this.threshold) {
const elapsed = Date.now() - this.lastFailure;
return elapsed < this.timeout;
}
return false;
}
private onSuccess() {
this.failures = 0;
}
private onFailure() {
this.failures++;
this.lastFailure = Date.now();
}
}
const breaker = new CircuitBreaker();
async function makeProtectedRequest() {
return breaker.call(() =>
client.responses.create({
model: 'gpt-4o',
input: 'Your prompt'
})
);
}

Implement comprehensive error logging:

async function makeRequestWithLogging(input: string) {
try {
const response = await client.responses.create({
model: 'gpt-4o',
input
});
// Log success
console.log({
timestamp: new Date().toISOString(),
level: 'info',
message: 'Request succeeded',
model: 'gpt-4o',
tokens: response.usage?.total_tokens
});
return response;
} catch (error: any) {
// Log error with full context
console.error({
timestamp: new Date().toISOString(),
level: 'error',
message: 'Request failed',
error_type: error.type,
error_message: error.message,
status_code: error.status,
provider: error.provider,
model: 'gpt-4o',
input_length: input.length
});
throw error;
}
}

Track error rates and patterns:

class ErrorMetrics {
private errors: Map<string, number> = new Map();
private total = 0;
record(errorType: string) {
this.total++;
this.errors.set(errorType, (this.errors.get(errorType) || 0) + 1);
}
getErrorRate(errorType: string): number {
return (this.errors.get(errorType) || 0) / this.total;
}
getMostCommon(): [string, number][] {
return Array.from(this.errors.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 5);
}
}
const metrics = new ErrorMetrics();
// Use in your error handlers
catch (error: any) {
metrics.record(error.type);
// ... handle error
}
  1. Always Handle Errors - Never leave errors unhandled
  2. Use Specific Error Types - Check error.type not just status codes
  3. Implement Retries - Use exponential backoff for transient errors
  4. Log with Context - Include request details in error logs
  5. Monitor Error Rates - Track error patterns over time
  6. Fail Gracefully - Provide fallback behavior when possible
  7. Respect Rate Limits - Use retry_after header values
  8. Circuit Breakers - Prevent cascading failures
  9. User-Friendly Messages - Don’t expose technical errors to end users
  10. Alert on Patterns - Set up alerts for unusual error rates
// Use auto-routing to automatically fallback
const response = await client.responses.create({
model: 'auto:balance', // Automatically picks healthy provider
input: 'Your prompt'
});
try {
const response = await client.responses.create({
model: 'gpt-4o',
input: 'Your prompt'
});
} catch (error: any) {
if (error.status === 429 && error.message.includes('quota')) {
// Notify user to upgrade plan
console.log('Daily quota exceeded. Upgrade plan for higher limits.');
}
}
try {
const response = await client.responses.create({
model: 'gpt-4o',
input: '' // Empty input
});
} catch (error: any) {
if (error.status === 422) {
console.log('Validation error:', error.errors);
// Fix input and retry
}
}