Error Handling
All API errors follow a consistent JSON response format with appropriate HTTP status codes.
Error Response Format
{
"error": "error_code",
"message": "Human readable error message"
}
HTTP Status Codes
| Status | Description |
|---|---|
400 | Bad Request - Invalid request data |
401 | Unauthorized - Missing or invalid authentication |
403 | Forbidden - Insufficient permissions |
404 | Not Found - Resource doesn't exist |
409 | Conflict - Resource already exists |
429 | Too Many Requests - Rate limit exceeded (future) |
500 | Internal Server Error - Unexpected error |
Authentication Errors (401)
Missing Authorization Header
{
"error": "unauthorized",
"message": "Missing Authorization header"
}
Invalid Token Format
{
"error": "unauthorized",
"message": "Invalid Authorization header format. Expected: Bearer {token}"
}
Invalid or Expired Token
{
"error": "unauthorized",
"message": "Invalid access token"
}
Revoked Token
{
"error": "unauthorized",
"message": "Token has been revoked"
}
Suspended App
{
"error": "unauthorized",
"message": "External application has been suspended"
}
Permission Errors (403)
Insufficient Scope
{
"error": "forbidden",
"message": "Insufficient permissions. Required scope: read"
}
Validation Errors (400)
Invalid Request Data
{
"error": "validation_error",
"message": "Invalid request data",
"details": [
{
"path": ["externalCourseId"],
"message": "Required"
}
]
}
Resource Errors
Not Found (404)
{
"error": "not_found",
"message": "Course not found or not linked to this external app"
}
Already Exists (409)
{
"error": "course_already_exists",
"message": "Course with externalCourseId 'xyz' already exists",
"existingCourse": {
"id": "course_123",
"name": "Example DGC"
}
}
Server Errors (500)
{
"error": "internal_server_error",
"message": "An unexpected error occurred"
}
Best Practices
1. Check HTTP Status First
if (!response.ok) {
const error = await response.json()
console.error(`Error ${response.status}:`, error.message)
}
2. Handle Specific Error Codes
const error = await response.json()
switch (error.error) {
case 'unauthorized':
// Refresh token or re-authenticate
break
case 'forbidden':
// Request additional permissions
break
case 'not_found':
// Handle missing resource
break
case 'validation_error':
// Show validation errors to user
break
}
3. Implement Retry Logic
async function fetchWithRetry(url, options, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options)
if (response.status === 401) {
// Don't retry auth errors
throw new Error('Authentication failed')
}
if (response.ok) {
return response
}
// Retry on 500 errors with exponential backoff
if (response.status >= 500) {
await new Promise(resolve =>
setTimeout(resolve, Math.pow(2, i) * 1000)
)
continue
}
// Don't retry client errors
throw new Error(`HTTP ${response.status}`)
} catch (error) {
if (i === maxRetries - 1) throw error
}
}
}
4. Log Error Details
try {
const response = await fetch(url, options)
if (!response.ok) {
const error = await response.json()
console.error('API Error:', {
status: response.status,
error: error.error,
message: error.message,
url,
timestamp: new Date().toISOString()
})
}
} catch (error) {
console.error('Network Error:', error)
}
Troubleshooting
Token expired
Solution: Use refresh token to get a new access token
Token revoked
Solution: User needs to re-authorize your application
Permission denied
Solution: Request appropriate scopes during authorization
Resource not found
Solution: Verify the resource ID and ensure it's linked to your app
Next Steps
- Authentication - Token management
- API Endpoints - Available endpoints
- Webhooks - Handle webhook errors