Error Handling
Handle API errors gracefully with detailed error codes and messages.
Pro Feature
The Kanman API uses standard HTTP status codes and returns detailed error information in JSON format.
Error Response Format
All errors follow this structure:
{
"error": {
"code": "error_code",
"message": "Human-readable error description",
"details": {}
}
}
| Field | Type | Description |
|---|---|---|
code |
string | Machine-readable error code |
message |
string | Human-readable description |
details |
object | Additional context (optional) |
HTTP Status Codes
Success Codes
| Status | Meaning |
|---|---|
| 200 | OK - Request succeeded |
| 201 | Created - Resource created |
Client Error Codes
| Status | Meaning |
|---|---|
| 400 | Bad Request - Invalid input |
| 401 | Unauthorized - Invalid or missing token |
| 403 | Forbidden - Insufficient permissions |
| 404 | Not Found - Resource doesn’t exist |
| 429 | Too Many Requests - Rate limit exceeded |
Server Error Codes
| Status | Meaning |
|---|---|
| 500 | Internal Server Error - Something went wrong |
| 503 | Service Unavailable - Temporary outage |
Common Error Codes
Authentication Errors
invalid_token
{
"error": {
"code": "invalid_token",
"message": "The provided API token is invalid"
}
}
Cause: Token doesn’t exist or is malformed
Solution: Check token is correct, hasn’t been revoked
token_expired
{
"error": {
"code": "token_expired",
"message": "The API token has expired",
"details": {
"expired_at": "2024-01-15T00:00:00Z"
}
}
}
Cause: Token past expiration date
Solution: Create a new token
insufficient_scope
{
"error": {
"code": "insufficient_scope",
"message": "Token lacks required scope",
"details": {
"required": "write",
"provided": ["read"]
}
}
}
Cause: Token doesn’t have permission for this operation
Solution: Create token with required scope
Resource Errors
board_not_found
{
"error": {
"code": "board_not_found",
"message": "Board not found or access denied",
"details": {
"board_id": "550e8400-e29b-41d4-a716-446655440000"
}
}
}
project_not_found
{
"error": {
"code": "project_not_found",
"message": "Project not found or access denied"
}
}
task_not_found
{
"error": {
"code": "task_not_found",
"message": "Task not found or access denied"
}
}
Validation Errors
invalid_request
{
"error": {
"code": "invalid_request",
"message": "Request validation failed",
"details": {
"fields": {
"label": "Label is required",
"status": "Status must be 0, 1, or 2"
}
}
}
}
invalid_uuid
{
"error": {
"code": "invalid_uuid",
"message": "Invalid UUID format",
"details": {
"field": "projectId",
"value": "not-a-uuid"
}
}
}
Quota Errors
quota_exceeded
{
"error": {
"code": "quota_exceeded",
"message": "Monthly API quota exceeded",
"details": {
"limit": 12000,
"used": 12000,
"reset": "2024-02-01T00:00:00Z"
}
}
}
resource_limit_exceeded
{
"error": {
"code": "resource_limit_exceeded",
"message": "Task limit reached for your plan",
"details": {
"resource": "tasks",
"limit": 500,
"current": 500
}
}
}
Handling Errors
JavaScript Example
async function apiRequest(url, options = {}) {
const response = await fetch(url, {
...options,
headers: {
'Authorization': `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json',
...options.headers
}
});
const data = await response.json();
if (!response.ok) {
const error = new Error(data.error?.message || 'API request failed');
error.code = data.error?.code;
error.status = response.status;
error.details = data.error?.details;
throw error;
}
return data;
}
// Usage
try {
const task = await apiRequest('https://api.kanman.de/v1/tasks/123');
console.log(task);
} catch (error) {
switch (error.code) {
case 'task_not_found':
console.log('Task does not exist');
break;
case 'token_expired':
console.log('Please refresh your API token');
break;
case 'quota_exceeded':
console.log(`Quota reset at ${error.details.reset}`);
break;
default:
console.error('API error:', error.message);
}
}
Python Example
import requests
class KanmanAPIError(Exception):
def __init__(self, code, message, status, details=None):
self.code = code
self.message = message
self.status = status
self.details = details or {}
super().__init__(message)
def api_request(method, endpoint, **kwargs):
response = requests.request(
method,
f'https://api.kanman.de/v1{endpoint}',
headers={'Authorization': f'Bearer {API_TOKEN}'},
**kwargs
)
data = response.json()
if not response.ok:
error = data.get('error', {})
raise KanmanAPIError(
code=error.get('code', 'unknown'),
message=error.get('message', 'API error'),
status=response.status_code,
details=error.get('details')
)
return data
# Usage
try:
task = api_request('GET', '/tasks/123')
except KanmanAPIError as e:
if e.code == 'task_not_found':
print('Task does not exist')
elif e.code == 'quota_exceeded':
print(f"Quota resets at {e.details['reset']}")
else:
print(f'Error: {e.message}')
Retry Strategy
For transient errors (5xx), implement exponential backoff:
async function apiRequestWithRetry(url, options, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
// Don't retry client errors
if (response.status >= 400 && response.status < 500) {
return response;
}
// Retry server errors
if (response.status >= 500) {
throw new Error(`Server error: ${response.status}`);
}
return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
// Exponential backoff: 1s, 2s, 4s
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
Related Topics
- Authentication - Token errors
- Rate Limits - Quota errors
- Tasks API - Task-specific errors
Last updated: January 1, 0001
Try Kanman