Error Codes
API error codes and handling
The Arctic API returns errors with HTTP status codes and JSON error bodies.
{
"error": "human-readable message",
"code": "ERROR_CODE"
}
| Status | Description |
|---|
| 400 | Bad Request - Invalid input |
| 401 | Unauthorized - Authentication required |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource does not exist |
| 409 | Conflict - Resource already exists |
| 422 | Unprocessable Entity - Validation error |
| 500 | Internal Server Error |
| Code | Status | Description |
|---|
UNAUTHORIZED | 401 | Token missing or invalid |
TOKEN_EXPIRED | 401 | Token has expired |
FORBIDDEN | 403 | Insufficient scope |
| Code | Status | Description |
|---|
NOT_FOUND | 404 | Resource not found |
ALREADY_EXISTS | 409 | Resource already exists |
ALREADY_BOOTSTRAPPED | 409 | Agent already initialized |
| Code | Status | Description |
|---|
INVALID_REQUEST | 400 | Malformed request body |
INVALID_CIDR | 400 | Invalid CIDR notation |
MISSING_FIELD | 400 | Required field missing |
INVALID_TRANSPORT | 400 | Transport must be tcp or kcp |
| Code | Status | Description |
|---|
NODE_LIMIT_EXCEEDED | 422 | License node limit reached |
SERVICE_LIMIT_EXCEEDED | 422 | License service limit reached |
| Code | Status | Description |
|---|
LICENSE_EXPIRED | 422 | License has expired |
LICENSE_INVALID | 422 | Invalid license signature |
LICENSE_MISMATCH | 422 | License ID mismatch |
| Code | Status | Description |
|---|
HANDSHAKE_FAILED | 422 | Peer handshake failed |
PEER_UNREACHABLE | 422 | Cannot connect to peer |
const response = await fetch('/v1/peers', {
method: 'POST',
headers: { 'Authorization': `Bearer ${token}` },
body: JSON.stringify({ address: '192.168.1.20:8080' })
});
if (!response.ok) {
const error = await response.json();
switch (error.code) {
case 'ALREADY_EXISTS':
console.log('Peer already in cluster');
break;
case 'NODE_LIMIT_EXCEEDED':
console.log('Upgrade license for more nodes');
break;
case 'HANDSHAKE_FAILED':
console.log('Check network connectivity');
break;
default:
console.log(`Error: ${error.error}`);
}
}
async function apiCall(url, options, retries = 3) {
for (let i = 0; i < retries; i++) {
const response = await fetch(url, options);
if (response.ok) return response.json();
if (response.status >= 500) {
// Retry on server errors
await sleep(1000 * (i + 1));
continue;
}
// Don't retry client errors
const error = await response.json();
throw new Error(error.error);
}
throw new Error('Max retries exceeded');
}