API Error Troubleshooting
Troubleshooting guide for SCORM API errors and HTTP status codes.
Table of Contents
HTTP Status Codes
200 OK
Meaning: Request succeeded.
When: Successful GET, PUT, PATCH requests.
Action: Process response data normally.
201 Created
Meaning: Resource created successfully.
When: Successful POST requests that create resources.
Action: Use returned resource data.
400 Bad Request
Meaning: Invalid request parameters.
Common Causes:
- Missing required fields
- Invalid data types
- Validation errors
Solution:
// Check error details
const error = await response.json();
console.error('Validation errors:', error.details);
// Fix request
const correctedRequest = {
...request,
// Fix validation errors
};
401 Unauthorized
Meaning: Authentication failed.
Common Causes:
- Missing API key
- Invalid API key
- Expired API key
Solution:
// Verify API key
const apiKey = process.env.CONNECT_API_KEY;
if (!apiKey) {
throw new Error('API key not configured');
}
// Check key format
if (!apiKey.startsWith('scorm_')) {
throw new Error('Invalid API key format');
}
403 Forbidden
Meaning: Access denied.
Common Causes:
- Insufficient permissions
- Tenant mismatch
- Resource access denied
Solution:
// Check API key scopes
// Verify tenant ID
// Ensure resource belongs to tenant
404 Not Found
Meaning: Resource not found.
Common Causes:
- Invalid resource ID
- Resource deleted
- Wrong endpoint
Solution:
// Verify resource ID
// Check resource exists
// Verify endpoint URL
409 Conflict
Meaning: Version conflict or resource conflict.
Common Causes:
- Optimistic locking conflict
- Duplicate resource
- Concurrent modification
Solution:
// Implement retry logic
// Fetch latest version
// Merge changes correctly
413 Payload Too Large
Meaning: Request body too large.
Common Causes:
- File exceeds size limit
- Request body too large
Solution:
// Use multipart upload for large files
// Split large requests
// Compress data
429 Too Many Requests
Meaning: Rate limit exceeded.
Common Causes:
- Too many requests in time window
- Exceeded quota
Solution:
// Implement exponential backoff
// Reduce request frequency
// Cache responses
500 Internal Server Error
Meaning: Server error.
Common Causes:
- Temporary server issue
- Processing error
- Database error
Solution:
// Retry with exponential backoff
// Log error details
// Contact support if persistent
503 Service Unavailable
Meaning: Service temporarily unavailable.
Common Causes:
- Maintenance
- Overloaded server
- Temporary outage
Solution:
// Retry after delay
// Check status page
// Wait and retry
Error Response Format
All errors follow this format:
{
"error": "Human-readable error message",
"code": "ERROR_CODE",
"details": {
"field": "Additional context"
}
}
Example Error Responses
Validation Error:
{
"error": "Invalid request parameters",
"code": "INVALID_REQUEST",
"details": {
"tenant_id": "Must be a valid UUID",
"uploaded_by": "Required field"
}
}
Authentication Error:
{
"error": "Invalid API key",
"code": "INVALID_API_KEY"
}
Resource Error:
{
"error": "Package not found",
"code": "PACKAGE_NOT_FOUND",
"details": {
"package_id": "pkg_abc123"
}
}
Common Error Codes
Authentication Errors
| Code | Description | Solution |
|---|---|---|
INVALID_API_KEY |
API key is invalid | Verify API key, regenerate if needed |
API_KEY_EXPIRED |
API key has expired | Generate new API key |
API_KEY_REVOKED |
API key was revoked | Generate new API key |
INSUFFICIENT_SCOPE |
API key lacks required scope | Use API key with correct scopes |
Validation Errors
| Code | Description | Solution |
|---|---|---|
INVALID_REQUEST |
Request validation failed | Check request parameters |
MISSING_FIELD |
Required field missing | Include all required fields |
INVALID_FORMAT |
Invalid data format | Check data types and formats |
INVALID_UUID |
Invalid UUID format | Use valid UUID format |
Resource Errors
| Code | Description | Solution |
|---|---|---|
PACKAGE_NOT_FOUND |
Package doesn't exist | Verify package ID |
SESSION_NOT_FOUND |
Session doesn't exist | Verify session ID |
TENANT_MISMATCH |
Tenant doesn't match | Verify tenant ID |
RESOURCE_NOT_FOUND |
Resource doesn't exist | Verify resource ID |
Processing Errors
| Code | Description | Solution |
|---|---|---|
INVALID_SCORM_PACKAGE |
Package is invalid | Validate package structure |
PROCESSING_FAILED |
Processing error | Check package, retry |
STORAGE_ERROR |
Storage backend error | Retry, check storage status |
QUOTA_EXCEEDED |
Quota limit reached | Free up space or upgrade |
Rate Limiting
| Code | Description | Solution |
|---|---|---|
RATE_LIMIT_EXCEEDED |
Too many requests | Implement backoff, reduce frequency |
Error Handling Best Practices
1. Always Check Response Status
async function makeRequest(url: string, options: RequestInit) {
const response = await fetch(url, options);
if (!response.ok) {
const error = await response.json();
throw new APIError(error.error, error.code, response.status);
}
return response.json();
}
2. Implement Retry Logic
async function makeRequestWithRetry(
url: string,
options: RequestInit,
maxRetries = 3
) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const response = await fetch(url, options);
// Don't retry client errors (4xx)
if (response.status >= 400 && response.status < 500) {
throw new Error(`Client error: ${response.status}`);
}
// Retry server errors (5xx)
if (response.status >= 500) {
if (attempt < maxRetries - 1) {
await delay(Math.pow(2, attempt) * 1000);
continue;
}
}
return response;
} catch (error) {
if (attempt === maxRetries - 1) throw error;
await delay(Math.pow(2, attempt) * 1000);
}
}
}
3. Handle Rate Limits
async function makeRequestWithRateLimit(url: string, options: RequestInit) {
const response = await fetch(url, options);
if (response.status === 429) {
const retryAfter = parseInt(
response.headers.get('Retry-After') || '60'
);
console.log(`Rate limited. Waiting ${retryAfter} seconds...`);
await new Promise(resolve =>
setTimeout(resolve, retryAfter * 1000)
);
return makeRequestWithRateLimit(url, options);
}
return response;
}
4. Handle Version Conflicts
async function updateWithVersionConflict(
updateFn: () => Promise<Response>,
maxRetries = 3
) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
const response = await updateFn();
if (response.ok) {
return await response.json();
}
if (response.status === 409 && attempt < maxRetries - 1) {
// Version conflict - fetch latest and retry
await delay(100 * (attempt + 1));
continue;
}
throw new Error(`Update failed: ${response.status}`);
}
}
5. Log Errors Appropriately
async function handleAPIError(error: any, context: string) {
// Log for debugging
console.error(`[${context}] API Error:`, {
message: error.message,
code: error.code,
status: error.status,
details: error.details
});
// Don't expose internal errors to users
if (error.status >= 500) {
return 'Service temporarily unavailable. Please try again later.';
}
// Return user-friendly messages
return error.message || 'An error occurred';
}
Error Handling Examples
Complete Error Handler
class APIError extends Error {
constructor(
message: string,
public code: string,
public status: number,
public details?: any
) {
super(message);
this.name = 'APIError';
}
}
async function handleAPIRequest<T>(
requestFn: () => Promise<Response>
): Promise<T> {
try {
const response = await requestFn();
if (!response.ok) {
const error = await response.json();
throw new APIError(
error.error || 'Request failed',
error.code || 'UNKNOWN_ERROR',
response.status,
error.details
);
}
return await response.json();
} catch (error) {
if (error instanceof APIError) {
throw error;
}
// Network or other errors
throw new APIError(
'Network error',
'NETWORK_ERROR',
0,
{ originalError: error }
);
}
}
Usage
try {
const package = await handleAPIRequest(() =>
fetch('/api/v1/packages/pkg_abc123', {
headers: { 'X-API-Key': apiKey }
})
);
console.log('Package:', package);
} catch (error) {
if (error instanceof APIError) {
switch (error.code) {
case 'PACKAGE_NOT_FOUND':
console.error('Package not found');
break;
case 'RATE_LIMIT_EXCEEDED':
console.error('Rate limit exceeded');
break;
default:
console.error('API error:', error.message);
}
} else {
console.error('Unexpected error:', error);
}
}
Related Documentation
- Error Code Reference - Complete error code catalog
- Common Issues - General troubleshooting
- Rate Limiting Guide - Rate limiting details
Last Updated: 2025-01-15