SCORM API Error Code Reference
SCORM API Error Code Reference
Overview
This document provides a comprehensive reference for all error codes returned by the SCORM API. Error codes are stable, machine-readable identifiers that can be used for programmatic error handling.
Error Response Format
All error responses follow this structure:
{
"error": "Human-readable error message",
"code": "ERROR_CODE",
"details": {
"field": "Additional context"
}
}
HTTP Status Codes
| Code | Meaning | Common Error Codes |
|---|---|---|
200 | Success | - |
201 | Created | - |
400 | Bad Request | INVALID_REQUEST, MISSING_FILE, INVALID_FILE_TYPE, FILE_TOO_LARGE, MANIFEST_NOT_FOUND, PARSE_FAILED |
401 | Unauthorized | UNAUTHORIZED, INVALID_API_KEY, EXPIRED_API_KEY |
403 | Forbidden | FORBIDDEN, INSUFFICIENT_SCOPES, TENANT_MISMATCH, QUOTA_EXCEEDED, DISPATCH_NOT_ACTIVE |
404 | Not Found | PACKAGE_NOT_FOUND, SESSION_NOT_FOUND, DISPATCH_NOT_FOUND, WEBHOOK_NOT_FOUND |
409 | Conflict | VERSION_CONFLICT, DUPLICATE_ENTRY |
413 | Payload Too Large | FILE_TOO_LARGE |
429 | Too Many Requests | RATE_LIMIT_EXCEEDED |
500 | Internal Server Error | INTERNAL_ERROR, DB_QUERY_FAILED, UPLOAD_FAILED, PARSE_FAILED |
503 | Service Unavailable | SERVICE_UNAVAILABLE, STORAGE_UNAVAILABLE |
Authentication Errors
UNAUTHORIZED
HTTP Status: 401
Description: Request missing or invalid authentication credentials.
Causes:
- Missing
X-API-Keyheader - Invalid API key
- Expired API key
- Malformed authentication header
Resolution:
// Check API key is included
const response = await fetch(url, {
headers: {
'X-API-Key': apiKey // Ensure this is set
}
});
// Verify API key is valid
if (response.status === 401) {
// Regenerate API key or check expiration
}
INVALID_API_KEY
HTTP Status: 401
Description: The provided API key is invalid or has been revoked.
Resolution:
- Verify API key is correct
- Check if key was revoked
- Generate a new API key if needed
EXPIRED_API_KEY
HTTP Status: 401
Description: The API key has expired.
Resolution:
- Generate a new API key
- Update your application with the new key
FORBIDDEN
HTTP Status: 403
Description: Request authenticated but lacks required permissions.
Causes:
- API key doesn't have required scopes
- Attempting to access another tenant's data
- Resource access denied
Resolution:
- Verify API key has required scopes (read/write/admin)
- Check tenant_id matches API key's tenant
- Request appropriate scopes if needed
INSUFFICIENT_SCOPES
HTTP Status: 403
Description: API key doesn't have required scope for this operation.
Example:
- Attempting
POSTwithread-only API key - Attempting
DELETEwithoutadminscope
Resolution:
- Use API key with appropriate scopes
- Request scope upgrade if needed
Request Validation Errors
INVALID_REQUEST
HTTP Status: 400
Description: Request body or parameters are invalid.
Common Causes:
- Missing required fields
- Invalid data types
- Validation rule violations
Example Response:
{
"error": "Invalid request parameters",
"code": "INVALID_REQUEST",
"details": {
"field": "tenant_id",
"message": "tenant_id is required"
}
}
Resolution:
- Review request body against API documentation
- Check all required fields are present
- Validate data types match expected format
MISSING_FILE
HTTP Status: 400
Description: File upload request missing file data.
Resolution:
// Ensure file is included in FormData
const formData = new FormData();
formData.append('file', fileBlob); // Required
formData.append('tenant_id', tenantId);
formData.append('uploaded_by', userId);
INVALID_FILE_TYPE
HTTP Status: 400
Description: Uploaded file is not a valid ZIP archive.
Resolution:
- Ensure file is a
.ziparchive - Verify file is not corrupted
- Check file extension matches content
FILE_TOO_LARGE
HTTP Status: 400 or 413
Description: Uploaded file exceeds maximum size limit (default 10GB).
Details:
{
"error": "File too large. Your file is 12GB, but the maximum size is 10GB",
"code": "FILE_TOO_LARGE",
"details": {
"file_size_gb": 12,
"max_size_gb": 10
}
}
Resolution:
- Reduce file size (compress assets, remove unused files)
- Use multipart upload for large files
- Request quota increase if justified
Resource Errors
PACKAGE_NOT_FOUND
HTTP Status: 404
Description: Requested package does not exist or is not accessible.
Causes:
- Package ID doesn't exist
- Package belongs to different tenant
- Package was deleted
Resolution:
// Verify package ID is correct
const package = await getPackage(packageId);
// Check tenant access
if (!package || package.tenant_id !== tenantId) {
// Package not found or access denied
}
SESSION_NOT_FOUND
HTTP Status: 404
Description: Requested session does not exist or is not accessible.
Resolution:
- Verify session ID is correct
- Check session belongs to your tenant
- Ensure session wasn't deleted
DISPATCH_NOT_FOUND
HTTP Status: 404
Description: Requested dispatch package does not exist.
Resolution:
- Verify dispatch ID is correct
- Check dispatch belongs to your tenant
- Ensure dispatch wasn't deleted
WEBHOOK_NOT_FOUND
HTTP Status: 404
Description: Requested webhook does not exist.
Resolution:
- Verify webhook ID is correct
- Check webhook belongs to your tenant
Conflict Errors
VERSION_CONFLICT
HTTP Status: 409
Description: Optimistic locking conflict - session was updated by another client.
Response:
{
"error": "Version conflict detected. Another update was made to this session.",
"code": "VERSION_CONFLICT",
"current_version": 2,
"message": "Please fetch the latest session data and retry your update."
}
Resolution:
async function updateSessionWithRetry(sessionId: string, updates: any) {
for (let attempt = 0; attempt < 3; attempt++) {
// Get latest session
const session = await getSession(sessionId);
// Merge updates
const merged = { ...session.cmi_data, ...updates.cmi_data };
// Update with current version
try {
return await updateSession(sessionId, {
...updates,
cmi_data: merged,
version: session.version
});
} catch (error) {
if (error.code === 'VERSION_CONFLICT' && attempt < 2) {
continue; // Retry
}
throw error;
}
}
}
DUPLICATE_ENTRY
HTTP Status: 409
Description: Attempting to create a resource that already exists.
Resolution:
- Check if resource already exists
- Use update endpoint if resource exists
- Use unique identifiers
Quota Errors
QUOTA_EXCEEDED
HTTP Status: 403
Description: Tenant quota limit exceeded.
Types:
TENANT_PACKAGE_LIMIT: Package count quota exceededTENANT_STORAGE_LIMIT: Storage quota exceeded
Response:
{
"error": "Storage quota exceeded. Used: 50 GB / Limit: 50 GB",
"code": "QUOTA_EXCEEDED",
"details": {
"quota_type": "storage",
"limit": 53687091200,
"used": 53687091200,
"required": 1048576000,
"available": 0
}
}
Resolution:
- Delete unused packages
- Request quota increase
- Optimize package sizes
- Archive old packages
Rate Limiting Errors
RATE_LIMIT_EXCEEDED
HTTP Status: 429
Description: Rate limit exceeded for tenant.
Response:
{
"error": "Rate limit exceeded",
"code": "RATE_LIMIT_EXCEEDED",
"details": {
"limit": 1000,
"remaining": 0,
"reset_at": "2025-01-12T10:00:00Z",
"retry_after_seconds": 3600
}
}
Headers:
Retry-After: 3600
X-RateLimit-Reset: 1640995200
Resolution:
- Wait for rate limit window to reset
- Implement exponential backoff
- Reduce request frequency
- Use webhooks instead of polling
Processing Errors
PARSE_FAILED
HTTP Status: 400 or 500
Description: Failed to parse SCORM manifest or package structure.
Causes:
- Invalid or corrupted manifest XML
- Missing required manifest elements
- Unsupported SCORM version
Resolution:
- Validate SCORM package before upload
- Check manifest XML is well-formed
- Ensure package follows SCORM specification
MANIFEST_NOT_FOUND
HTTP Status: 400
Description: imsmanifest.xml not found in package.
Resolution:
- Ensure package contains
imsmanifest.xmlat root - Verify package structure is correct
- Check package is not corrupted
UPLOAD_FAILED
HTTP Status: 500
Description: Failed to upload file to storage.
Causes:
- Storage service unavailable
- Network connectivity issues
- Storage quota exceeded
Resolution:
- Retry with exponential backoff
- Check storage service status
- Verify storage configuration
- Check storage quota
Database Errors
DB_QUERY_FAILED
HTTP Status: 500
Description: Database query failed.
Causes:
- Database connection issues
- Query timeout
- Constraint violations
- RLS policy violations
Resolution:
- Retry with exponential backoff
- Check database connectivity
- Verify RLS policies are configured
- Review query parameters
DB_INSERT_FAILED
HTTP Status: 500
Description: Failed to insert record into database.
Resolution:
- Check required fields are provided
- Verify data types match schema
- Check for constraint violations
- Retry if transient error
Dispatch Errors
DISPATCH_NOT_ACTIVE
HTTP Status: 403
Description: Dispatch package is not active (expired or revoked).
Resolution:
- Check dispatch expiration date
- Verify dispatch is not revoked
- Create new dispatch if expired
REGISTRATION_LIMIT_EXCEEDED
HTTP Status: 403
Description: Dispatch registration limit reached.
Resolution:
- Check registration count
- Increase registration limit if needed
- Create new dispatch for additional registrations
LICENSE_LIMIT_EXCEEDED
HTTP Status: 403
Description: Dispatch license limit reached.
Resolution:
- Check license count
- Increase license limit if needed
- Verify unique user tracking
Webhook Errors
WEBHOOK_DELIVERY_FAILED
HTTP Status: 500
Description: Failed to deliver webhook event.
Causes:
- Webhook URL unreachable
- Webhook server error
- Network timeout
- Invalid webhook signature
Resolution:
- Verify webhook URL is accessible
- Check webhook server logs
- Verify HMAC signature verification
- System will retry automatically
SCORM API Errors (Player)
SCORM 1.2 Error Codes
| Code | Description |
|---|---|
0 | No error |
101 | General exception |
201 | Invalid argument error |
202 | Element cannot have children |
203 | Element not an array - cannot have count |
301 | Not initialized |
401 | Not implemented error |
402 | Invalid set value, element is a keyword |
403 | Element is read only |
404 | Element is write only |
405 | Data model element value not initialized |
SCORM 2004 Error Codes
| Code | Description |
|---|---|
0 | No error |
101 | General exception |
102 | General initialization failure |
103 | Already initialized |
104 | Content instance terminated |
111 | General termination failure |
112 | Termination before initialization |
113 | Termination after termination |
122 | Retrieve data before initialization |
123 | Retrieve data after termination |
132 | Store data before initialization |
133 | Store data after termination |
142 | Commit before initialization |
143 | Commit after termination |
201 | Invalid argument error |
301 | General get failure |
351 | General set failure |
391 | General commit failure |
401 | Undefined data model element |
402 | Unimplemented data model element |
403 | Data model element value not initialized |
404 | Data model element is read only |
405 | Data model element is write only |
406 | Data model element type mismatch |
407 | Data model element value out of range |
408 | Data model dependency not established |
Error Handling Best Practices
1. Check Error Codes
const response = await fetch(url, options);
const data = await response.json();
if (!response.ok) {
switch (data.code) {
case 'VERSION_CONFLICT':
// Retry with fresh data
return retryUpdate();
case 'RATE_LIMIT_EXCEEDED':
// Wait and retry
await wait(data.details.retry_after_seconds);
return retry();
case 'QUOTA_EXCEEDED':
// Handle quota limit
throw new QuotaExceededError(data);
default:
throw new APIError(data);
}
}
2. Implement Retry Logic
async function requestWithRetry(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) except 409
if (response.status >= 400 && response.status < 500 && response.status !== 409) {
return response;
}
// Retry server errors (5xx) and 409 conflicts
if (response.ok || (response.status !== 500 && response.status !== 503 && response.status !== 409)) {
return response;
}
// Exponential backoff
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
} catch (error) {
if (attempt === maxRetries - 1) throw error;
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
3. Log Errors for Debugging
function handleError(error: APIError) {
console.error('API Error:', {
code: error.code,
message: error.message,
details: error.details,
status: error.status,
request_id: error.request_id
});
// Send to error tracking service
errorTracker.captureException(error);
}
Error Code Categories
Client Errors (4xx)
- Authentication:
UNAUTHORIZED,INVALID_API_KEY,FORBIDDEN - Validation:
INVALID_REQUEST,MISSING_FILE,INVALID_FILE_TYPE - Resources:
PACKAGE_NOT_FOUND,SESSION_NOT_FOUND - Conflicts:
VERSION_CONFLICT,DUPLICATE_ENTRY - Quotas:
QUOTA_EXCEEDED - Rate Limits:
RATE_LIMIT_EXCEEDED
Server Errors (5xx)
- Processing:
PARSE_FAILED,UPLOAD_FAILED - Database:
DB_QUERY_FAILED,DB_INSERT_FAILED - Services:
SERVICE_UNAVAILABLE,STORAGE_UNAVAILABLE - General:
INTERNAL_ERROR
Last Updated: 2025-01-12
Version: 1.0
For error handling examples, see Error Handling Guide.