Error Handling
The Mantis API returns structured error responses following a consistent format. This guide covers all error codes, their meanings, and how to resolve them.
Error Response Format
Section titled “Error Response Format”All errors follow this JSON structure:
{ "error": { "code": "ERROR_CODE", "message": "Human-readable error description", "details": { // Optional: Additional context (only for specific errors) } }}Fields:
code- Machine-readable error identifier (constant)message- Human-readable description (may vary)details- Optional additional context
HTTP Status Codes
Section titled “HTTP Status Codes”| Status Code | Meaning | Usage |
|---|---|---|
400 | Bad Request | Validation errors, malformed requests |
401 | Unauthorized | Missing or invalid authentication |
403 | Forbidden | Insufficient permissions, access denied |
404 | Not Found | Resource doesn’t exist |
409 | Conflict | Duplicate resource, constraint violation |
429 | Too Many Requests | Rate limit exceeded |
500 | Internal Server Error | Unexpected server-side error |
503 | Service Unavailable | External service (database, queue) unavailable |
Authentication Errors (401)
Section titled “Authentication Errors (401)”UNAUTHORIZED
Section titled “UNAUTHORIZED”HTTP Status: 401
Description: General authentication failure.
Example:
{ "error": { "code": "UNAUTHORIZED", "message": "Authentication required" }}Causes:
- No
Authorizationheader provided - Empty authorization header
Resolution: Include the Authorization: Bearer <token> header in your request.
INVALID_CREDENTIALS
Section titled “INVALID_CREDENTIALS”HTTP Status: 401
Description: Invalid email/password combination or API key.
Example:
{ "error": { "code": "INVALID_CREDENTIALS", "message": "Invalid credentials" }}Causes:
- Wrong email or password
- Account is inactive or suspended
- API key is revoked or expired
Resolution:
- Verify credentials are correct
- Check account status with administrator
- Create a new API key if expired
TOKEN_EXPIRED
Section titled “TOKEN_EXPIRED”HTTP Status: 401
Description: JWT access token has expired.
Example:
{ "error": { "code": "TOKEN_EXPIRED", "message": "Token expired" }}Resolution:
- Call
POST /auth/refreshto get a new access token - Implement automatic token refresh in your client
INVALID_TOKEN
Section titled “INVALID_TOKEN”HTTP Status: 401
Description: Token is malformed or signature is invalid.
Example:
{ "error": { "code": "INVALID_TOKEN", "message": "Invalid token: Invalid user ID in token" }}Causes:
- Malformed JWT structure
- Invalid signature (wrong secret key)
- Token was modified after signing
- Token audience/issuer mismatch
Resolution:
- Login again to get a fresh token
- Verify you’re connecting to the correct Mantis server
MISSING_AUTH_HEADER
Section titled “MISSING_AUTH_HEADER”HTTP Status: 401
Description: The Authorization header is missing.
Example:
{ "error": { "code": "MISSING_AUTH_HEADER", "message": "Missing authorization header" }}Resolution: Add the header:
Authorization: Bearer <your-token>INVALID_AUTH_HEADER
Section titled “INVALID_AUTH_HEADER”HTTP Status: 401
Description: The Authorization header format is incorrect.
Example:
{ "error": { "code": "INVALID_AUTH_HEADER", "message": "Invalid authorization header format" }}Causes:
- Missing “Bearer” prefix
- Extra spaces or special characters
- Incorrect encoding
Valid format:
Authorization: Bearer eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImN1cnJlbnQifQ...TWO_FACTOR_REQUIRED
Section titled “TWO_FACTOR_REQUIRED”HTTP Status: 403
Description: Pending 2FA token used on a protected endpoint.
Example:
{ "error": { "code": "TWO_FACTOR_REQUIRED", "message": "Two-factor authentication required" }}Cause: You received a pending_token from login and tried to use it on a regular endpoint.
Resolution: Complete the 2FA flow by calling POST /auth/2fa/verify first.
Authorization Errors (403)
Section titled “Authorization Errors (403)”FORBIDDEN
Section titled “FORBIDDEN”HTTP Status: 403
Description: Access denied with specific reason.
Example:
{ "error": { "code": "FORBIDDEN", "message": "Access denied: Account is suspended" }}Causes:
- Account status is not “active”
- Tenant-specific access restrictions
- Resource belongs to a different tenant
Resolution: Contact your administrator to check account status and permissions.
INSUFFICIENT_PERMISSIONS
Section titled “INSUFFICIENT_PERMISSIONS”HTTP Status: 403
Description: User lacks required role or permission.
Example:
{ "error": { "code": "INSUFFICIENT_PERMISSIONS", "message": "Insufficient permissions" }}Causes:
- Missing required role (e.g., “admin”, “operator”)
- Missing required permission (e.g., “deployments:create”)
- Tenant isolation prevents access
Resolution:
- Request the required role from your administrator
- Verify you’re in the correct tenant context
Validation Errors (400)
Section titled “Validation Errors (400)”VALIDATION_ERROR
Section titled “VALIDATION_ERROR”HTTP Status: 400
Description: Request data failed validation rules.
Example:
{ "error": { "code": "VALIDATION_ERROR", "message": "Validation failed: email: Invalid email format" }}Causes:
- Missing required fields
- Invalid field formats (email, UUID, etc.)
- Out-of-range values
- Field length constraints violated
Resolution: Review the validation message and fix the request body.
Common validation rules:
- Email must be valid format
- UUIDs must be properly formatted
- Pagination:
page >= 1,limitbetween 1-100 - Names typically 1-255 characters
INVALID_BODY
Section titled “INVALID_BODY”HTTP Status: 400
Description: Request body is not valid JSON.
Example:
{ "error": { "code": "INVALID_BODY", "message": "Invalid request body: expected value at line 1 column 1" }}Causes:
- Malformed JSON syntax
- Missing closing braces/brackets
- Wrong encoding
Resolution: Validate your JSON with a linter before sending.
MISSING_FIELD
Section titled “MISSING_FIELD”HTTP Status: 400
Description: Required field is missing from request.
Example:
{ "error": { "code": "MISSING_FIELD", "message": "Missing required field: solution_id" }}Resolution: Add the missing field to your request body.
Resource Errors (404)
Section titled “Resource Errors (404)”NOT_FOUND
Section titled “NOT_FOUND”HTTP Status: 404
Description: Requested resource doesn’t exist.
Example:
{ "error": { "code": "NOT_FOUND", "message": "Resource not found: Deployment not found" }}Causes:
- Invalid resource ID
- Resource was deleted
- Tenant isolation (resource exists but in different tenant)
- Resource never existed
Resolution:
- Verify the resource ID is correct
- Check if the resource was deleted
- Confirm you’re in the correct tenant
Conflict Errors (409)
Section titled “Conflict Errors (409)”CONFLICT
Section titled “CONFLICT”HTTP Status: 409
Description: Resource already exists or constraint violated.
Example:
{ "error": { "code": "CONFLICT", "message": "Resource already exists: User with email test@example.com already exists" }}Causes:
- Unique constraint violation (duplicate email, username, name)
- Foreign key constraint violation
- Check constraint violation
Resolution:
- Use a different unique identifier
- Delete the existing resource first (if appropriate)
- Update the existing resource instead of creating a new one
DEPLOYMENT_FROZEN
Section titled “DEPLOYMENT_FROZEN”HTTP Status: 409
Description: Deployment is blocked by an active deployment freeze.
Example:
{ "error": { "code": "DEPLOYMENT_FROZEN", "message": "Deployment blocked by active freeze", "details": { "active_freezes": [ { "id": "f1e2d3c4-b5a6-7890-abcd-ef1234567890", "name": "Production Freeze", "ends_at": "2024-12-26T23:59:59Z" } ], "can_override": false } }}Causes:
- Active freeze targeting the environment or tags
- Insufficient permissions to override freeze
Resolution:
- Wait until freeze ends
- Request override permission from administrator
- Use the
override_freeze: trueparameter (if you have override permission)
Rate Limiting (429)
Section titled “Rate Limiting (429)”Mantis has two independent rate limiters. They return different bodies, so handle
both by status code (429) rather than by parsing a specific JSON shape.
General API limiter
Section titled “General API limiter”HTTP Status: 429
Description: Per-client-IP limit exceeded (100 requests/second sustained, burst 200).
The body is plain text, not JSON:
Rate limit exceeded. Please try again later.Response headers:
Retry-After: 1X-RateLimit-Remaining: 0Authentication limiter (AUTH_RATE_LIMIT_EXCEEDED)
Section titled “Authentication limiter (AUTH_RATE_LIMIT_EXCEEDED)”HTTP Status: 429
Description: Too many authentication attempts (5 login attempts/minute per IP). Exceeding the limit triggers a temporary lockout.
The body is JSON:
{ "error": { "code": "AUTH_RATE_LIMIT_EXCEEDED", "message": "Too many authentication attempts", "retry_after_secs": 900, "reason": "too_many_attempts" }}Response headers:
Retry-After: 900X-RateLimit-Remaining: 0Content-Type: application/jsonResolution:
- Honor the
Retry-Afterheader before retrying - Implement exponential backoff in your client
- Reduce request frequency
- Contact your administrator if you need higher general limits
Database Errors (500)
Section titled “Database Errors (500)”DATABASE_ERROR
Section titled “DATABASE_ERROR”HTTP Status: 500
Description: Database operation failed.
Example:
{ "error": { "code": "DATABASE_ERROR", "message": "A database error occurred" }}Causes:
- Database server is down
- Query timeout
- Database corruption (rare)
Resolution:
- Retry the request
- Contact administrator if persistent
DATABASE_CONNECTION_ERROR
Section titled “DATABASE_CONNECTION_ERROR”HTTP Status: 500
Description: Cannot establish database connection.
Example:
{ "error": { "code": "DATABASE_CONNECTION_ERROR", "message": "Database connection error: connection pool exhausted" }}Causes:
- Database server unreachable
- Connection pool exhausted
- Network issues
Resolution:
- Wait and retry
- Contact administrator for infrastructure issues
Service Errors (500/503)
Section titled “Service Errors (500/503)”INTERNAL_ERROR
Section titled “INTERNAL_ERROR”HTTP Status: 500
Description: Unexpected server error.
Example:
{ "error": { "code": "INTERNAL_ERROR", "message": "Internal server error" }}Resolution:
- Retry the request
- Report to administrator with request details
- Check server logs if you have access
SERVICE_UNAVAILABLE
Section titled “SERVICE_UNAVAILABLE”HTTP Status: 503
Description: External service is unavailable.
Example:
{ "error": { "code": "SERVICE_UNAVAILABLE", "message": "Service unavailable: Orchestration service unreachable" }}Causes:
- Thorax (gRPC orchestration service) is down
- RabbitMQ message queue is unavailable
- Redis cache is down
Resolution:
- Check service health endpoints
- Contact administrator for infrastructure issues
EXTERNAL_SERVICE_ERROR
Section titled “EXTERNAL_SERVICE_ERROR”HTTP Status: 500
Description: External service returned an error.
Example:
{ "error": { "code": "EXTERNAL_SERVICE_ERROR", "message": "External service error: Failed to dispatch deployment" }}Resolution:
- Retry the request
- Check external service status
- Contact administrator if persistent
CONFIGURATION_ERROR
Section titled “CONFIGURATION_ERROR”HTTP Status: 500
Description: Server configuration is invalid.
Example:
{ "error": { "code": "CONFIGURATION_ERROR", "message": "Configuration error: Invalid JWT secret" }}Resolution: Contact your administrator immediately. This indicates a server misconfiguration.
Troubleshooting Tips
Section titled “Troubleshooting Tips”Debugging Authentication Issues
Section titled “Debugging Authentication Issues”-
Check token format:
Terminal window # Decode JWT (without verification) to inspect claimsecho "eyJhbGciOiJFUzI..." | cut -d'.' -f2 | base64 -d 2>/dev/null | jq -
Verify token expiration:
const token = 'eyJhbGci...';const payload = JSON.parse(atob(token.split('.')[1]));console.log('Expires at:', new Date(payload.exp * 1000)); -
Test with curl:
Terminal window curl -v https://mantis.example.com/api/v1/auth/me \-H "Authorization: Bearer $TOKEN"
Handling Errors in Code
Section titled “Handling Errors in Code”JavaScript/TypeScript:
async function handleApiCall(url: string, token: string) { const response = await fetch(url, { headers: { Authorization: `Bearer ${token}` }, });
if (!response.ok) { const error = await response.json();
switch (error.error.code) { case 'TOKEN_EXPIRED': // Trigger token refresh await refreshToken(); return handleApiCall(url, newToken); // Retry
case 'INSUFFICIENT_PERMISSIONS': // Show permission error to user showError("You don't have permission for this action"); break;
case 'NOT_FOUND': // Handle missing resource return null;
default: throw new Error(error.error.message); } }
return response.json();}Python:
import requestsfrom typing import Optional, Dict, Any
def handle_api_error(response: requests.Response) -> None: """Parse and handle API error responses.""" if response.status_code == 401: error = response.json() if error['error']['code'] == 'TOKEN_EXPIRED': # Trigger token refresh raise TokenExpiredError() raise AuthenticationError(error['error']['message'])
elif response.status_code == 403: raise PermissionError('Insufficient permissions')
elif response.status_code == 404: return None # Resource not found
elif response.status_code >= 500: raise ServiceError('Server error, please retry')
response.raise_for_status()Next Steps
Section titled “Next Steps”- Authentication Guide - Learn how to authenticate
- Pagination - Handle paginated responses
- Deployments API - Start using the API
