Error Handling
Understand API error codes, responses, and how to handle errors gracefully.
Overview
The ArcanFlows API uses conventional HTTP status codes and returns detailed error information in JSON format to help you handle errors gracefully.
Error Response Format
All errors follow a consistent JSON structure:
json{ "error": { "code": "error_code", "message": "Human-readable error description", "details": { "field": "specific_field", "reason": "additional_context" }, "request_id": "req_abc123xyz" } }
Fields
| Field | Type | Description |
|---|---|---|
code | string | Machine-readable error code |
message | string | Human-readable description |
details | object | Additional context (optional) |
request_id | string | Unique request ID for support |
HTTP Status Codes
Success Codes (2xx)
| Code | Status | Description |
|---|---|---|
200 | OK | Request succeeded |
201 | Created | Resource created successfully |
202 | Accepted | Request accepted for processing |
204 | No Content | Request succeeded, no content returned |
Client Error Codes (4xx)
| Code | Status | Description |
|---|---|---|
400 | Bad Request | Invalid request format or parameters |
401 | Unauthorized | Missing or invalid API key |
403 | Forbidden | Valid key but insufficient permissions |
404 | Not Found | Resource doesn't exist |
409 | Conflict | Resource conflict (e.g., duplicate) |
422 | Unprocessable Entity | Validation failed |
429 | Too Many Requests | Rate limit exceeded |
Server Error Codes (5xx)
| Code | Status | Description |
|---|---|---|
500 | Internal Server Error | Unexpected server error |
502 | Bad Gateway | Upstream service error |
503 | Service Unavailable | Temporary unavailability |
504 | Gateway Timeout | Request timed out |
Error Codes Reference
Authentication Errors
unauthorized
json{ "error": { "code": "unauthorized", "message": "Invalid API key provided", "details": { "reason": "api_key_invalid" } } }
Causes:
- Missing Authorization header
- Invalid or malformed API key
- Expired API key
- Deleted API key
Solution: Check your API key and ensure it's correctly formatted.
forbidden
json{ "error": { "code": "forbidden", "message": "API key lacks required scope", "details": { "required_scope": "agents:write", "available_scopes": ["agents:read"] } } }
Solution: Create a new API key with the required scopes.
Validation Errors
validation_error
json{ "error": { "code": "validation_error", "message": "Validation failed", "details": { "errors": [ { "field": "name", "message": "Name is required", "code": "required" }, { "field": "model", "message": "Invalid model: 'gpt-5'. Allowed: gpt-4, gpt-3.5-turbo, claude-3-sonnet", "code": "invalid_enum" } ] } } }
Common validation codes:
required- Field is requiredinvalid_type- Wrong data typeinvalid_enum- Value not in allowed listmin_length- String too shortmax_length- String too longmin_value- Number too smallmax_value- Number too largeinvalid_format- Invalid format (email, URL, etc.)
invalid_request
json{ "error": { "code": "invalid_request", "message": "Request body must be valid JSON", "details": { "parse_error": "Unexpected token at position 42" } } }
Resource Errors
not_found
json{ "error": { "code": "not_found", "message": "Agent not found", "details": { "resource_type": "agent", "resource_id": "agent_123abc" } } }
conflict
json{ "error": { "code": "conflict", "message": "An agent with this name already exists", "details": { "field": "name", "value": "Support Bot" } } }
resource_exhausted
json{ "error": { "code": "resource_exhausted", "message": "Agent limit reached for your plan", "details": { "resource": "agents", "limit": 10, "current": 10, "plan": "pro" } } }
Rate Limit Errors
rate_limited
json{ "error": { "code": "rate_limited", "message": "Rate limit exceeded", "details": { "limit": 300, "window_seconds": 60, "retry_after": 23 } } }
Execution Errors
execution_failed
json{ "error": { "code": "execution_failed", "message": "Workflow execution failed", "details": { "execution_id": "exec_abc123", "node_id": "http_request_1", "node_error": "Connection timeout after 30s" } } }
execution_timeout
json{ "error": { "code": "execution_timeout", "message": "Workflow execution timed out", "details": { "execution_id": "exec_abc123", "timeout_seconds": 300, "elapsed_seconds": 300 } } }
AI/Model Errors
model_error
json{ "error": { "code": "model_error", "message": "AI model request failed", "details": { "provider": "openai", "model": "gpt-4", "provider_error": "Rate limit exceeded" } } }
context_length_exceeded
json{ "error": { "code": "context_length_exceeded", "message": "Input exceeds model context length", "details": { "model": "gpt-4", "max_tokens": 8192, "input_tokens": 9500 } } }
Server Errors
internal_error
json{ "error": { "code": "internal_error", "message": "An unexpected error occurred", "request_id": "req_abc123xyz" } }
Action: Retry the request. If persistent, contact support with the request_id.
service_unavailable
json{ "error": { "code": "service_unavailable", "message": "Service temporarily unavailable", "details": { "retry_after": 60 } } }
Handling Errors
JavaScript/TypeScript
typescriptinterface APIError { error: { code: string; message: string; details?: Record<string, unknown>; request_id?: string; }; } async function apiRequest(url: string, options: RequestInit) { const response = await fetch(url, options); if (!response.ok) { const error: APIError = await response.json(); switch (error.error.code) { case 'unauthorized': throw new AuthenticationError(error.error.message); case 'forbidden': throw new PermissionError(error.error.message); case 'validation_error': throw new ValidationError(error.error.message, error.error.details); case 'not_found': throw new NotFoundError(error.error.message); case 'rate_limited': const retryAfter = error.error.details?.retry_after || 60; throw new RateLimitError(error.error.message, retryAfter); default: throw new APIError(error.error.message, error.error.code); } } return response.json(); } // Usage try { const agent = await apiRequest('/v1/agents', { method: 'POST', body: JSON.stringify({ name: 'My Agent' }), }); } catch (error) { if (error instanceof ValidationError) { // Show validation errors to user console.error('Validation failed:', error.details); } else if (error instanceof RateLimitError) { // Wait and retry await sleep(error.retryAfter * 1000); } else { // Log and show generic error console.error('API error:', error); } }
Python
pythonimport requests from dataclasses import dataclass from typing import Optional, Dict, Any @dataclass class APIError(Exception): code: str message: str details: Optional[Dict[str, Any]] = None request_id: Optional[str] = None def api_request(method: str, url: str, **kwargs) -> dict: response = requests.request(method, url, **kwargs) if not response.ok: error_data = response.json().get('error', {}) raise APIError( code=error_data.get('code', 'unknown'), message=error_data.get('message', 'Unknown error'), details=error_data.get('details'), request_id=error_data.get('request_id'), ) return response.json() # Usage try: agent = api_request('POST', '/v1/agents', json={'name': 'My Agent'}) except APIError as e: if e.code == 'validation_error': print(f"Validation failed: {e.details}") elif e.code == 'rate_limited': retry_after = e.details.get('retry_after', 60) print(f"Rate limited. Retry after {retry_after}s") else: print(f"API error: {e.message} (request_id: {e.request_id})")
Retry Logic
javascriptconst RETRYABLE_ERRORS = [ 'rate_limited', 'internal_error', 'service_unavailable', 'gateway_timeout', ]; async function apiRequestWithRetry(url, options, maxRetries = 3) { let lastError; for (let attempt = 0; attempt < maxRetries; attempt++) { try { return await apiRequest(url, options); } catch (error) { lastError = error; if (!RETRYABLE_ERRORS.includes(error.code)) { throw error; // Don't retry non-retryable errors } const delay = error.details?.retry_after || Math.pow(2, attempt) * 1000; console.log(`Attempt ${attempt + 1} failed. Retrying in ${delay}ms...`); await sleep(delay); } } throw lastError; }
Debugging Tips
1. Use Request IDs
Always log the request_id from error responses:
javascriptcatch (error) { console.error(`API Error [request_id: ${error.request_id}]: ${error.message}`); }
Provide this ID when contacting support.
2. Check Response Headers
javascriptconst response = await fetch(url, options); console.log('X-Request-ID:', response.headers.get('X-Request-ID')); console.log('X-RateLimit-Remaining:', response.headers.get('X-RateLimit-Remaining'));
3. Validate Locally First
Before sending to the API, validate your data:
javascriptfunction validateAgent(data) { const errors = []; if (!data.name) errors.push({ field: 'name', message: 'Required' }); if (data.name?.length > 100) errors.push({ field: 'name', message: 'Max 100 chars' }); const validModels = ['gpt-4', 'gpt-3.5-turbo', 'claude-3-sonnet']; if (!validModels.includes(data.model)) { errors.push({ field: 'model', message: `Must be one of: ${validModels.join(', ')}` }); } if (errors.length > 0) { throw new ValidationError('Validation failed', errors); } }
4. Enable Debug Logging
javascriptconst DEBUG = process.env.DEBUG === 'true'; async function apiRequest(url, options) { if (DEBUG) { console.log('Request:', { url, ...options }); } const response = await fetch(url, options); if (DEBUG) { console.log('Response:', { status: response.status, headers: Object.fromEntries(response.headers), }); } return response; }
Common Mistakes
1. Not Checking Error Type
javascript// ❌ Wrong - treats all errors the same try { await apiRequest('/v1/agents', options); } catch (error) { alert('Something went wrong!'); } // ✅ Right - handle different error types try { await apiRequest('/v1/agents', options); } catch (error) { if (error.code === 'validation_error') { showValidationErrors(error.details.errors); } else if (error.code === 'unauthorized') { redirectToLogin(); } else { showGenericError(error.message); } }
2. Ignoring Rate Limits
javascript// ❌ Wrong - no rate limit handling await Promise.all(ids.map(id => fetch(`/v1/agents/${id}`))); // ✅ Right - respect rate limits const rateLimiter = new Bottleneck({ minTime: 100 }); await Promise.all(ids.map(id => rateLimiter.schedule(() => fetch(`/v1/agents/${id}`)) ));
3. Not Logging Request IDs
javascript// ❌ Wrong - hard to debug console.error('API failed:', error.message); // ✅ Right - includes request ID console.error(`API failed [request_id: ${error.request_id}]: ${error.message}`);