This reference documents all error codes returned by the License Monitor API with troubleshooting guidance.
All API errors follow a consistent JSON format:
"error": "Error Category",
"message": "Human-readable error description",
"details": "Additional context if available",
"requestId": "req-abc123",
"timestamp": "2024-01-15T10:30:45.123Z"
| Code | Meaning | Description |
|---|
| 200 | OK | Request successful |
| 201 | Created | Resource created |
| 204 | No Content | Success with no response body |
| Code | Meaning | Common Causes |
|---|
| 400 | Bad Request | Invalid JSON, missing required fields |
| 401 | Unauthorized | Missing or invalid API key |
| 403 | Forbidden | Valid key but insufficient permissions |
| 404 | Not Found | Endpoint or resource doesn’t exist |
| 405 | Method Not Allowed | Wrong HTTP method |
| 409 | Conflict | Resource conflict |
| 422 | Unprocessable Entity | Validation error |
| 429 | Too Many Requests | Rate limit exceeded |
| Code | Meaning | Common Causes |
|---|
| 500 | Internal Server Error | Unexpected server error |
| 502 | Bad Gateway | Upstream service unavailable |
| 503 | Service Unavailable | Server overloaded or maintenance |
| 504 | Gateway Timeout | Upstream timeout |
| Code | HTTP | Description | Resolution |
|---|
AUTH_MISSING | 401 | No API key provided | Include X-API-Key header |
AUTH_INVALID | 401 | Invalid API key | Verify API key is correct |
AUTH_EXPIRED | 401 | API key expired | Generate new API key |
AUTH_REVOKED | 401 | API key revoked | Contact administrator |
AUTH_INSUFFICIENT | 403 | Insufficient permissions | Use key with proper permissions |
| Code | HTTP | Description | Resolution |
|---|
INVALID_JSON | 400 | Malformed JSON body | Check JSON syntax |
MISSING_FIELD | 400 | Required field missing | Include required field |
INVALID_FIELD | 400 | Field value invalid | Check field value type/format |
INVALID_QUERY | 400 | Invalid query parameter | Check parameter syntax |
BODY_TOO_LARGE | 413 | Request body too large | Reduce payload size |
| Code | HTTP | Description | Resolution |
|---|
NOT_FOUND | 404 | Resource not found | Verify resource ID/path |
ALREADY_EXISTS | 409 | Resource already exists | Use different identifier |
CONFLICT | 409 | Resource conflict | Resolve conflict |
GONE | 410 | Resource deleted | Resource no longer available |
| Code | HTTP | Description | Resolution |
|---|
RATE_LIMIT_EXCEEDED | 429 | Too many requests | Wait and retry |
QUOTA_EXCEEDED | 429 | Daily/monthly quota exceeded | Upgrade plan or wait |
| Code | HTTP | Description | Resolution |
|---|
INTERNAL_ERROR | 500 | Unexpected error | Report issue with requestId |
DATABASE_ERROR | 500 | Database operation failed | Retry or report |
UPSTREAM_ERROR | 502 | License server unavailable | Check license server status |
SERVICE_UNAVAILABLE | 503 | Service temporarily unavailable | Retry later |
TIMEOUT | 504 | Operation timed out | Retry or increase timeout |
| Code | HTTP | Description | Resolution |
|---|
LICENSE_SERVER_UNREACHABLE | 502 | Cannot connect to license server | Check server connectivity |
LICENSE_QUERY_FAILED | 500 | License query returned error | Check server logs |
LICENSE_PARSE_ERROR | 500 | Cannot parse license output | Report parsing issue |
COMMAND_EXECUTION_FAILED | 500 | Command execution failed | Check command and permissions |
COMMAND_TIMEOUT | 504 | Command timed out | Increase timeout or simplify command |
async function fetchWithErrorHandling(url: string, options: RequestInit = {}) {
const response = await fetch(url, options);
const error: ApiError = await response.json();
throw new Error('Authentication required. Please provide a valid API key.');
case 'RATE_LIMIT_EXCEEDED':
const retryAfter = response.headers.get('Retry-After');
throw new Error(`Rate limited. Retry after ${retryAfter} seconds.`);
case 'LICENSE_SERVER_UNREACHABLE':
throw new Error('License server is not reachable. Please check connectivity.');
throw new Error(`API Error: ${error.message} (${error.code})`);
class ApiError(Exception):
def __init__(self, code, message, request_id):
self.request_id = request_id
super().__init__(f"{code}: {message}")
def api_request(url, headers=None):
response = requests.get(url, headers=headers)
if error['code'] == 'RATE_LIMIT_EXCEEDED':
retry_after = response.headers.get('Retry-After', 60)
f"Rate limited. Retry after {retry_after}s",
These errors are safe to retry:
| Code | Retry Strategy |
|---|
RATE_LIMIT_EXCEEDED | Wait for Retry-After header |
SERVICE_UNAVAILABLE | Exponential backoff |
TIMEOUT | Retry with same timeout |
UPSTREAM_ERROR | Retry after delay |
DATABASE_ERROR | Retry after short delay |
These errors require action before retrying:
| Code | Required Action |
|---|
AUTH_MISSING | Add authentication |
AUTH_INVALID | Fix API key |
INVALID_JSON | Fix request body |
MISSING_FIELD | Add required field |
NOT_FOUND | Verify resource exists |
async function retryWithBackoff<T>(
for (let attempt = 0; attempt < maxRetries; attempt++) {
// Check if error is retryable
if (!isRetryable(error)) {
// Calculate delay with jitter
const delay = baseDelay * Math.pow(2, attempt) * (0.5 + Math.random() * 0.5);
await new Promise(resolve => setTimeout(resolve, delay));
function isRetryable(error: any): boolean {
return retryableCodes.includes(error.code);
Every response includes a requestId for debugging:
# Include request ID when reporting issues
curl -v http://localhost:8080/api/health
< X-Request-ID: req-abc123def456
"requestId": "req-abc123def456",
Use the request ID to find related logs:
grep "req-abc123def456" /var/log/license-monitor/license_monitor.log