Skip to main content

Overview

SO1 APIs use standard HTTP status codes and structured error responses to communicate failures. All error responses follow a consistent format for easy parsing and handling.

Error Response Format

All errors return JSON with this structure:
{
  "success": false,
  "error": {
    "code": "error_code",
    "message": "Human-readable error message",
    "details": {
      "field": "specific_field",
      "reason": "detailed_explanation"
    }
  },
  "metadata": {
    "timestamp": "2026-03-10T15:30:00Z",
    "request_id": "req_abc123"
  }
}

HTTP Status Codes

2xx Success

CodeStatusMeaning
200OKRequest succeeded
201CreatedResource created successfully
202AcceptedRequest accepted for async processing
204No ContentRequest succeeded with no response body

4xx Client Errors

CodeStatusMeaning
400Bad RequestInvalid request format or parameters
401UnauthorizedMissing or invalid authentication
403ForbiddenAuthenticated but lacking permissions
404Not FoundResource doesn’t exist
409ConflictRequest conflicts with current state
422Unprocessable EntityValidation failed
429Too Many RequestsRate limit exceeded

5xx Server Errors

CodeStatusMeaning
500Internal Server ErrorUnexpected server failure
502Bad GatewayUpstream service error
503Service UnavailableTemporary unavailability
504Gateway TimeoutUpstream service timeout

Error Codes Reference

Authentication Errors

invalid_api_key

Status: 401 The provided API key is not valid or has been revoked.
{
  "error": {
    "code": "invalid_api_key",
    "message": "The API key provided is invalid",
    "details": {
      "key_prefix": "cp_live_sk_"
    }
  }
}
Resolution:
  • Verify the API key is correct
  • Check the key hasn’t been revoked in Console
  • Ensure you’re using the correct environment key

expired_api_key

Status: 401 The API key has expired.
{
  "error": {
    "code": "expired_api_key",
    "message": "The API key has expired",
    "details": {
      "expired_at": "2026-01-01T00:00:00Z"
    }
  }
}
Resolution:
  • Generate a new API key
  • Update your application configuration
  • Set longer expiration dates on keys

missing_authorization

Status: 401 No authentication credentials provided.
{
  "error": {
    "code": "missing_authorization",
    "message": "Authorization header is required"
  }
}
Resolution:
  • Add Authorization: Bearer YOUR_API_KEY header
  • Verify the header is being sent correctly

insufficient_permissions

Status: 403 API key doesn’t have required permissions.
{
  "error": {
    "code": "insufficient_permissions",
    "message": "Your API key lacks the required permissions",
    "details": {
      "required": ["agent:execute"],
      "has": ["workflow:read"]
    }
  }
}
Resolution:
  • Use an API key with appropriate permissions
  • Request permission upgrade from admin
  • Generate new key with required scopes

Validation Errors

validation_error

Status: 400 Request validation failed.
{
  "error": {
    "code": "validation_error",
    "message": "Request validation failed",
    "details": {
      "errors": [
        {
          "field": "workflow_name",
          "message": "Must be between 1 and 255 characters"
        },
        {
          "field": "agents",
          "message": "Must contain at least one agent"
        }
      ]
    }
  }
}
Resolution:
  • Review the details.errors array
  • Fix each validation error
  • Ensure all required fields are provided

invalid_json

Status: 400 Request body is not valid JSON.
{
  "error": {
    "code": "invalid_json",
    "message": "Request body must be valid JSON",
    "details": {
      "parse_error": "Unexpected token } in JSON at position 45"
    }
  }
}
Resolution:
  • Validate JSON syntax
  • Use JSON.stringify() for objects
  • Check for trailing commas, unclosed brackets

missing_required_field

Status: 400 Required field is missing from request.
{
  "error": {
    "code": "missing_required_field",
    "message": "Required field is missing",
    "details": {
      "field": "agent_id",
      "location": "body"
    }
  }
}
Resolution:
  • Add the missing required field
  • Check API documentation for required parameters

Resource Errors

resource_not_found

Status: 404 The requested resource doesn’t exist.
{
  "error": {
    "code": "resource_not_found",
    "message": "The requested resource was not found",
    "details": {
      "resource_type": "workflow",
      "resource_id": "wf_nonexistent"
    }
  }
}
Resolution:
  • Verify the resource ID is correct
  • Check the resource wasn’t deleted
  • Ensure you have access to the resource

resource_already_exists

Status: 409 A resource with the same identifier already exists.
{
  "error": {
    "code": "resource_already_exists",
    "message": "A workflow with this name already exists",
    "details": {
      "field": "name",
      "value": "webhook-processing",
      "existing_id": "wf_abc123"
    }
  }
}
Resolution:
  • Use a different name/identifier
  • Update the existing resource instead
  • Delete the existing resource first (if appropriate)

resource_locked

Status: 409 Resource is locked and cannot be modified.
{
  "error": {
    "code": "resource_locked",
    "message": "Resource is currently locked",
    "details": {
      "resource_id": "wf_abc123",
      "locked_by": "user_xyz789",
      "locked_until": "2026-03-10T16:00:00Z"
    }
  }
}
Resolution:
  • Wait for lock to expire
  • Contact the user who locked the resource
  • Use force_unlock parameter (if authorized)

Rate Limiting Errors

rate_limit_exceeded

Status: 429 Too many requests in a given time period.
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "details": {
      "limit": 1000,
      "window": "1h",
      "retry_after": 3600
    }
  }
}
Headers:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1678464000
Retry-After: 3600
Resolution:
  • Wait for rate limit window to reset
  • Implement exponential backoff
  • Upgrade to higher tier for more requests
  • Cache responses to reduce API calls

Execution Errors

agent_execution_failed

Status: 500 Agent execution failed unexpectedly.
{
  "error": {
    "code": "agent_execution_failed",
    "message": "Agent execution failed",
    "details": {
      "agent_id": "workflow-architect",
      "execution_id": "exec_abc123",
      "reason": "LLM API timeout",
      "retryable": true
    }
  }
}
Resolution:
  • Retry the request (if retryable: true)
  • Check agent status page
  • Contact support if issue persists

timeout

Status: 504 Request exceeded maximum execution time.
{
  "error": {
    "code": "timeout",
    "message": "Request timed out after 30 seconds",
    "details": {
      "timeout_ms": 30000,
      "elapsed_ms": 30125
    }
  }
}
Resolution:
  • Break request into smaller operations
  • Use async workflows for long-running tasks
  • Increase timeout (if configurable)

invalid_agent_configuration

Status: 400 Agent configuration is invalid.
{
  "error": {
    "code": "invalid_agent_configuration",
    "message": "Invalid agent configuration",
    "details": {
      "field": "temperature",
      "value": 2.5,
      "valid_range": "0.0 to 2.0"
    }
  }
}
Resolution:
  • Review agent configuration documentation
  • Adjust parameters to valid ranges
  • Remove unsupported configuration options

Workflow Errors

workflow_validation_failed

Status: 422 Workflow failed validation checks.
{
  "error": {
    "code": "workflow_validation_failed",
    "message": "Workflow validation failed",
    "details": {
      "errors": [
        {
          "type": "circular_dependency",
          "agents": ["agent-a", "agent-b", "agent-a"]
        },
        {
          "type": "missing_dependency",
          "agent": "agent-c",
          "required_by": "agent-d"
        }
      ]
    }
  }
}
Resolution:
  • Fix circular dependencies
  • Add missing agent dependencies
  • Validate workflow structure

forge_gate_failure

Status: 422 FORGE gate validation failed.
{
  "error": {
    "code": "forge_gate_failure",
    "message": "FORGE gate validation failed",
    "details": {
      "gate": "design_exit",
      "failed_criteria": [
        {
          "criterion": "test_coverage",
          "required": 0.80,
          "actual": 0.65
        }
      ]
    }
  }
}
Resolution:
  • Address failed criteria
  • Re-run gate validation
  • Request gate override (if authorized)

Service Errors

service_unavailable

Status: 503 Service is temporarily unavailable.
{
  "error": {
    "code": "service_unavailable",
    "message": "Service is temporarily unavailable",
    "details": {
      "reason": "maintenance",
      "expected_available_at": "2026-03-10T16:00:00Z"
    }
  }
}
Resolution:

internal_server_error

Status: 500 Unexpected internal error.
{
  "error": {
    "code": "internal_server_error",
    "message": "An internal error occurred",
    "details": {
      "request_id": "req_abc123"
    }
  }
}
Resolution:
  • Retry the request
  • Contact support with request_id
  • Check status page for incidents

Error Handling Best Practices

1. Always Check Status Codes

const response = await fetch(url, options);

if (!response.ok) {
  const error = await response.json();
  throw new APIError(error);
}

const data = await response.json();

2. Implement Retry Logic

async function fetchWithRetry(url: string, options: RequestInit, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      const response = await fetch(url, options);
      
      if (response.ok) {
        return await response.json();
      }
      
      // Don't retry client errors (4xx except 429)
      if (response.status >= 400 && response.status < 500 && response.status !== 429) {
        throw new APIError(await response.json());
      }
      
      // Exponential backoff
      const delay = Math.pow(2, i) * 1000;
      await new Promise(resolve => setTimeout(resolve, delay));
      
    } catch (error) {
      if (i === maxRetries - 1) throw error;
    }
  }
}

3. Handle Rate Limits

async function fetchWithRateLimit(url: string, options: RequestInit) {
  const response = await fetch(url, options);
  
  if (response.status === 429) {
    const retryAfter = parseInt(response.headers.get('Retry-After') || '60');
    
    console.log(`Rate limited. Retrying after ${retryAfter}s`);
    await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
    
    return fetchWithRateLimit(url, options);
  }
  
  return response;
}

4. Log Request IDs

async function logError(response: Response) {
  const error = await response.json();
  const requestId = error.metadata?.request_id;
  
  console.error(`API Error [${requestId}]:`, error);
  
  // Send to error tracking
  Sentry.captureException(new Error(error.error.message), {
    extra: {
      requestId,
      statusCode: response.status,
      errorCode: error.error.code
    }
  });
}

5. Provide User-Friendly Messages

function getErrorMessage(error: APIError): string {
  const errorMessages: Record<string, string> = {
    'invalid_api_key': 'Your session has expired. Please log in again.',
    'rate_limit_exceeded': 'Too many requests. Please try again in a few minutes.',
    'validation_error': 'Please check your input and try again.',
    'resource_not_found': 'The requested item could not be found.',
    'internal_server_error': 'Something went wrong. Please try again later.'
  };
  
  return errorMessages[error.code] || error.message;
}

Common Error Scenarios

Scenario 1: Invalid API Key

Request:
curl https://control-plane.so1.io/api/v1/workflows \
  -H "Authorization: Bearer invalid_key"
Response:
{
  "success": false,
  "error": {
    "code": "invalid_api_key",
    "message": "The API key provided is invalid"
  }
}
Fix:
# Use valid API key
curl https://control-plane.so1.io/api/v1/workflows \
  -H "Authorization: Bearer ${CONTROL_PLANE_API_KEY}"

Scenario 2: Missing Required Field

Request:
curl -X POST https://control-plane.so1.io/api/v1/workflows \
  -H "Authorization: Bearer ${CONTROL_PLANE_API_KEY}" \
  -d '{"name": "My Workflow"}'
Response:
{
  "error": {
    "code": "missing_required_field",
    "message": "Required field is missing",
    "details": {
      "field": "agents"
    }
  }
}
Fix:
curl -X POST https://control-plane.so1.io/api/v1/workflows \
  -H "Authorization: Bearer ${CONTROL_PLANE_API_KEY}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "My Workflow",
    "agents": [{"agent_id": "workflow-architect"}]
  }'

Scenario 3: Rate Limit Exceeded

Response:
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded",
    "details": {
      "retry_after": 300
    }
  }
}
Fix:
// Implement exponential backoff
const delay = 300; // seconds from retry_after
await new Promise(resolve => setTimeout(resolve, delay * 1000));
// Retry request