Error Handling
The Ascend API uses standard HTTP status codes and returns structured JSON error responses to help you diagnose and handle errors effectively.
Error Response Format
All error responses follow a consistent JSON structure:
{
"detail": "Human-readable error message",
"error_code": "MACHINE_READABLE_CODE",
"status": 400,
"correlation_id": "action_20260120_143052_abc12345",
"timestamp": "2026-01-20T14:30:52Z"
}
| Field | Type | Description |
|---|---|---|
detail | string | Human-readable error description |
error_code | string | Machine-readable error code for programmatic handling |
status | integer | HTTP status code |
correlation_id | string | Unique ID for request tracing (include in support tickets) |
timestamp | string | ISO 8601 timestamp of the error |
HTTP Status Codes
Success Codes
| Code | Name | Description |
|---|---|---|
| 200 | OK | Request succeeded |
| 201 | Created | Resource created successfully |
| 204 | No Content | Request succeeded, no response body (e.g., DELETE) |
Client Error Codes (4xx)
| Code | Name | Description |
|---|---|---|
| 400 | Bad Request | Invalid request syntax or parameters |
| 401 | Unauthorized | Missing or invalid authentication |
| 403 | Forbidden | Valid auth but insufficient permissions |
| 404 | Not Found | Resource does not exist |
| 409 | Conflict | Resource already exists or state conflict |
| 422 | Unprocessable Entity | Valid syntax but semantic errors |
| 429 | Too Many Requests | Rate limit exceeded |
Server Error Codes (5xx)
| Code | Name | Description |
|---|---|---|
| 500 | Internal Server Error | Unexpected server error |
| 502 | Bad Gateway | Upstream service unavailable |
| 503 | Service Unavailable | Service temporarily unavailable |
Common Error Codes
Authentication Errors
| Error Code | HTTP Status | Description | Resolution |
|---|---|---|---|
INVALID_API_KEY | 401 | API key is invalid or malformed | Check API key format and value |
EXPIRED_API_KEY | 401 | API key has expired | Generate a new API key |
REVOKED_API_KEY | 401 | API key has been revoked | Generate a new API key |
INVALID_TOKEN | 401 | JWT token is invalid or expired | Refresh or obtain new token |
MISSING_AUTH | 401 | No authentication provided | Include X-API-Key or Authorization header |
Example:
{
"detail": "API key has expired",
"error_code": "EXPIRED_API_KEY",
"status": 401,
"correlation_id": "auth_20260120_143052_def456"
}
Authorization Errors
| Error Code | HTTP Status | Description | Resolution |
|---|---|---|---|
INSUFFICIENT_PERMISSIONS | 403 | User lacks required permissions | Contact admin for role upgrade |
ADMIN_REQUIRED | 403 | Admin role required for this action | Use admin credentials |
ORGANIZATION_MISMATCH | 403 | Resource belongs to different org | Verify resource ownership |
Example:
{
"detail": "Admin role required to manage webhooks",
"error_code": "ADMIN_REQUIRED",
"status": 403
}
Validation Errors
| Error Code | HTTP Status | Description | Resolution |
|---|---|---|---|
MISSING_FIELD | 422 | Required field is missing | Include all required fields |
INVALID_FIELD_TYPE | 422 | Field has wrong data type | Check field type requirements |
INVALID_FIELD_VALUE | 422 | Field value is out of range | Check valid value ranges |
VALIDATION_ERROR | 422 | General validation failure | Review request body |
Example:
{
"detail": "Missing required fields: agent_id, action_type",
"error_code": "MISSING_FIELD",
"status": 422,
"missing_fields": ["agent_id", "action_type"]
}
Resource Errors
| Error Code | HTTP Status | Description | Resolution |
|---|---|---|---|
RESOURCE_NOT_FOUND | 404 | Requested resource doesn't exist | Verify resource ID |
AGENT_NOT_FOUND | 404 | Agent with given ID not found | Register agent first |
ACTION_NOT_FOUND | 404 | Action with given ID not found | Verify action ID |
ALREADY_EXISTS | 409 | Resource with same ID exists | Use unique identifier |
INVALID_STATE | 400 | Resource in invalid state for operation | Check current state |
Example:
{
"detail": "Action 12345 not found",
"error_code": "ACTION_NOT_FOUND",
"status": 404
}
Rate Limiting Errors
| Error Code | HTTP Status | Description | Resolution |
|---|---|---|---|
RATE_LIMIT_EXCEEDED | 429 | Request rate limit exceeded | Wait and retry with backoff |
AGENT_RATE_LIMIT | 429 | Per-agent rate limit exceeded | Reduce agent request frequency |
ORG_RATE_LIMIT | 429 | Organization rate limit exceeded | Contact support for limit increase |
Example:
{
"detail": "Rate limit exceeded: 100 requests per minute",
"error_code": "RATE_LIMIT_EXCEEDED",
"status": 429,
"retry_after": 45,
"limit": 100,
"remaining": 0,
"reset_at": "2026-01-20T14:31:00Z"
}
Governance Errors
| Error Code | HTTP Status | Description | Resolution |
|---|---|---|---|
ACTION_DENIED | 403 | Action blocked by policy | Review policy or request exemption |
APPROVAL_REQUIRED | 202 | Action requires human approval | Wait for approval decision |
SPEND_LIMIT_EXCEEDED | 402 | Organization spend limit reached | Contact billing admin |
KILL_SWITCH_ACTIVE | 402 | Organization kill-switch is active | Contact admin to reactivate |
Example:
{
"detail": "Action blocked by policy: No database writes during maintenance window",
"error_code": "ACTION_DENIED",
"status": 403,
"policy_name": "maintenance-window-policy",
"policy_id": 42
}
Error Handling Best Practices
1. Always Check Status Codes
import requests
response = requests.post(
"https://pilot.owkai.app/api/v1/actions/submit",
headers={"X-API-Key": api_key},
json=payload
)
if response.status_code == 200:
result = response.json()
# Handle success
elif response.status_code == 401:
# Handle authentication error
raise AuthenticationError(response.json()["detail"])
elif response.status_code == 429:
# Handle rate limiting with exponential backoff
retry_after = response.json().get("retry_after", 60)
time.sleep(retry_after)
elif response.status_code >= 500:
# Handle server errors with retry
raise ServerError(response.json()["detail"])
else:
# Handle other errors
raise APIError(response.json()["detail"])
2. Implement Exponential Backoff
import time
import random
def submit_with_retry(payload, max_retries=3):
for attempt in range(max_retries):
response = requests.post(url, json=payload)
if response.status_code == 200:
return response.json()
if response.status_code == 429:
# Exponential backoff with jitter
wait_time = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait_time)
continue
if response.status_code >= 500:
# Server error - retry with backoff
wait_time = (2 ** attempt) + random.uniform(0, 1)
time.sleep(wait_time)
continue
# Client error - don't retry
raise APIError(response.json())
raise MaxRetriesExceeded("Failed after maximum retries")
3. Log Correlation IDs
Always log the correlation_id from error responses for debugging:
try:
result = client.actions.submit(payload)
except APIError as e:
logger.error(
f"API error: {e.detail}",
extra={
"correlation_id": e.correlation_id,
"error_code": e.error_code,
"status": e.status
}
)
4. Handle Pending Approvals
When an action requires approval, poll for status:
result = client.actions.submit(payload)
if result["status"] == "pending_approval":
action_id = result["id"]
# Poll for approval decision
while True:
status = client.actions.get_status(action_id)
if status["decision_ready"]:
if status["status"] == "approved":
print("Action approved!")
break
elif status["status"] == "denied":
print("Action denied")
break
time.sleep(5) # Poll every 5 seconds
SDK Error Handling
Python SDK
from ascend import AscendClient
from ascend.exceptions import (
AuthenticationError,
RateLimitError,
ValidationError,
ActionDeniedError
)
client = AscendClient(api_key="owkai_...")
try:
result = client.actions.submit(
agent_id="my-agent",
action_type="database_query",
description="Query users",
tool_name="postgresql"
)
except AuthenticationError as e:
print(f"Auth failed: {e}")
except RateLimitError as e:
print(f"Rate limited. Retry after {e.retry_after}s")
except ValidationError as e:
print(f"Invalid request: {e.missing_fields}")
except ActionDeniedError as e:
print(f"Action denied by policy: {e.policy_name}")
Node.js SDK
import { AscendClient, AscendError } from '@ascend-ai/sdk';
const client = new AscendClient({ apiKey: 'owkai_...' });
try {
const result = await client.actions.submit({
agentId: 'my-agent',
actionType: 'database_query',
description: 'Query users',
toolName: 'postgresql'
});
} catch (error) {
if (error instanceof AscendError) {
console.error(`Error: ${error.message}`);
console.error(`Code: ${error.code}`);
console.error(`Correlation ID: ${error.correlationId}`);
if (error.code === 'RATE_LIMIT_EXCEEDED') {
await sleep(error.retryAfter * 1000);
// Retry...
}
}
}
Troubleshooting
"Invalid API Key" Error
- Verify the API key format:
owkai_{role}_{random} - Check that the key hasn't been revoked
- Ensure the key hasn't expired
- Confirm you're using the correct environment (production vs sandbox)
"Rate Limit Exceeded" Error
- Check the
retry_aftervalue in the response - Implement exponential backoff
- Consider batching requests
- Contact support if you need higher limits
"Action Denied" Error
- Review the policy that denied the action
- Check if the action type is allowed for your agent
- Verify the risk score isn't exceeding thresholds
- Request a policy exemption if needed
"Spend Limit Exceeded" Error
- Contact your billing administrator
- Review current usage in the admin console
- Request a limit increase if needed
500 Internal Server Error
- Note the
correlation_idfor support - Retry with exponential backoff
- Check the status page for outages
- Contact support if the issue persists