Appendix B: Error Codes & Solutions
This appendix provides a comprehensive reference for all error codes and messages you may encounter when working with Vibe CMS. Each error includes its cause, solution steps, and prevention tips.
HTTP Status Codes
400 Bad Request
Invalid request parameters or malformed data.
Empty File Upload
Error Message:
File is empty
Cause: Attempting to upload a file with no content.
Solution:
- Verify the file exists and contains data before uploading
- Check file path is correct
- Ensure file wasn't corrupted during read operation
Prevention:
- Validate file size > 0 before upload
- Add client-side file validation
Example:
// Validate file before upload
if (!file || file.size === 0) {
throw new Error('Cannot upload empty file');
}
await uploadFile(file);
Invalid Status Value
Error Message:
Status must be one of: draft, published, archived
Cause: Attempting to set content item status to an invalid value.
Solution:
- Use only allowed status values:
draft,published, orarchived - Check for typos in status string
Prevention:
- Use TypeScript enums or constants for status values
- Add validation before API calls
Example:
const VALID_STATUSES = ['draft', 'published', 'archived'] as const;
type Status = typeof VALID_STATUSES[number];
function updateStatus(itemId: string, status: Status) {
// TypeScript ensures only valid values are passed
return api.updateContentItem(itemId, { status });
}
Missing Required Parameter
Error Message:
{parameter_name} parameter is required and cannot be empty
Cause: Required parameter is missing, null, or empty string.
Solution:
- Check API documentation for required parameters
- Ensure all required fields are provided
- Verify values aren't accidentally undefined or empty
Prevention:
- Use TypeScript interfaces to enforce required parameters
- Validate inputs before API calls
Example:
interface CreateCollectionParams {
projectId: string;
name: string;
slug: string;
description?: string;
}
async function createCollection(params: CreateCollectionParams) {
// TypeScript ensures required fields are present
return await api.createCollection(params);
}
Invalid Locale Code
Error Message:
Locale {locale_code} is not available for this project
Cause: Attempting to use a locale that hasn't been configured for the project.
Solution:
- Check available locales:
GET /api/locales?project_id={project_id} - Create the locale if needed
- Verify locale code format (e.g., 'en-US', 'de-DE')
Prevention:
- Fetch and cache available locales on app initialization
- Validate locale codes against project configuration
Example:
// Fetch available locales
const locales = await api.getProjectLocales(projectId);
const localeCodes = locales.map(l => l.locale_code);
// Validate before use
if (!localeCodes.includes(targetLocale)) {
throw new Error(`Locale ${targetLocale} not configured`);
}
Variants Only for Images
Error Message:
Variants are only available for images
Cause: Attempting to generate variants for non-image files.
Solution:
- Check file MIME type before requesting variants
- Use original file for non-image types
Prevention:
- Check
mime_type.startsWith('image/')before variant requests - Handle different file types appropriately
Example:
const file = await api.getFile(fileId);
if (file.mime_type.startsWith('image/')) {
// Request variant for images
return `/assets/${projectId}/${fileId}?variant=thumbnail`;
} else {
// Use original for non-images
return `/assets/${projectId}/${fileId}`;
}
401 Unauthorized
Authentication credentials are missing or invalid.
Missing Authentication
Error Message:
Authentication credentials were not provided
Cause: No JWT token or API key provided in request headers.
Solution:
- Include JWT token in Authorization header:
Bearer {token} - Or include API key in requests
- Check token hasn't been accidentally stripped
Prevention:
- Use SDK which handles authentication automatically
- Implement auth interceptor for HTTP client
- Store and refresh tokens properly
Example:
// Using SDK (handles auth automatically)
const cms = createClient({
projectId: 'your-project',
baseUrl: 'https://api.vibecms.com',
apiKey: 'your-api-key'
});
// Manual fetch with auth
const response = await fetch(url, {
headers: {
'Authorization': `Bearer ${jwtToken}`
}
});
Invalid Token Format
Error Message:
Invalid token format
Cause: JWT token is malformed or missing required claims.
Solution:
- Verify token is properly formatted (3 base64 segments separated by dots)
- Ensure token contains required fields (sub, email)
- Check token wasn't corrupted during storage/transmission
Prevention:
- Store tokens securely (httpOnly cookies or secure storage)
- Don't manually modify token strings
- Use proper JWT libraries
Expired Token
Error Message:
Token has expired
Cause: JWT token is past its expiration time.
Solution:
- Refresh the token using refresh token flow
- Re-authenticate user if refresh fails
- Clear expired tokens from storage
Prevention:
- Implement automatic token refresh before expiration
- Monitor token expiration timestamps
- Handle refresh failures gracefully
Example:
// Check token expiration before requests
function isTokenExpired(token: string): boolean {
const payload = JSON.parse(atob(token.split('.')[1]));
return payload.exp * 1000 < Date.now();
}
async function makeAuthRequest(url: string) {
if (isTokenExpired(currentToken)) {
currentToken = await refreshToken();
}
return fetch(url, {
headers: { 'Authorization': `Bearer ${currentToken}` }
});
}
Invalid or Expired Upload Token
Error Message:
Invalid or expired upload token
Cause: Upload token is invalid, expired, or already used.
Solution:
- Request a new upload token
- Check token expiration (typically 1 hour)
- Ensure token hasn't been used already
Prevention:
- Request tokens just before upload
- Don't reuse tokens
- Implement retry logic for expired tokens
403 Forbidden
Authentication succeeded but user lacks required permissions.
Insufficient Permissions
Error Message:
Access denied: insufficient permissions
Cause: User doesn't have required role or permissions for the operation.
Solution:
- Check user's account membership and role
- Request elevated permissions from account owner
- Verify correct project/account context
Prevention:
- Check user permissions before showing UI actions
- Display appropriate error messages to users
- Implement role-based access control in UI
Example:
// Check permissions before actions
const user = await api.getCurrentUser();
const canEdit = user.roles.includes('owner') ||
user.roles.includes('member');
if (canEdit) {
// Show edit button
} else {
// Show read-only message
}
RLS Policy Violation
Error Message:
Permission denied for this operation
Cause: Database Row-Level Security policy denied access.
Solution:
- Verify user is member of the account
- Check project belongs to user's account
- Ensure JWT token is valid and current
Prevention:
- Always use account/project IDs from user context
- Don't hardcode IDs from other accounts
404 Not Found
Requested resource doesn't exist or isn't accessible.
Collection Not Found
Error Message:
Collection not found
Cause: Collection ID or slug doesn't exist or user lacks access.
Solution:
- Verify collection exists in project
- Check collection slug spelling
- Confirm user has access to the project
Prevention:
- Validate collection IDs before use
- Handle 404s gracefully with user-friendly messages
- Cache valid collection IDs
Example:
try {
const items = await cms.collection('blog-posts').all();
} catch (error) {
if (error.status === 404) {
console.error('Collection "blog-posts" not found');
// Show user-friendly message or redirect
}
}
Content Item Not Found
Error Message:
Content item not found for locale {locale}
Cause: Content item doesn't exist or translation missing for locale.
Solution:
- Verify item ID is correct
- Check if item exists for other locales
- Create translation if item exists in different locale
Prevention:
- Check available locales before querying
- Implement fallback locale logic
- Handle missing translations gracefully
Example:
// Locale fallback strategy
async function getContentWithFallback(itemId: string, locale: string) {
try {
return await api.getContentItem(itemId, locale);
} catch (error) {
if (error.status === 404) {
// Try fallback locale
return await api.getContentItem(itemId, 'en-US');
}
throw error;
}
}
File Not Found
Error Message:
File not found
Cause: File ID doesn't exist or was deleted.
Solution:
- Verify file ID is correct
- Check if file was deleted
- Ensure user has access to the project
Prevention:
- Validate file references before use
- Handle deleted files gracefully
- Implement orphaned reference cleanup
Content Not Published
Error Message:
Content not found or not published
Cause: Content item exists but isn't in published status (via public API).
Solution:
- Publish the content item in CMS admin
- Use authenticated API for draft content
- Check content status in admin panel
Prevention:
- Only reference published content in public-facing apps
- Show draft previews only to authenticated users
409 Conflict
Request conflicts with current state or existing data.
Duplicate Collection Slug
Error Message:
{
"error": "Duplicate value",
"field": "slug",
"message": "A collection with this slug already exists in the project. Please choose a different slug.",
"constraint": "unique_slug_per_project"
}
Cause: Attempting to create collection with slug that already exists in project.
Solution:
- Choose a different slug
- Update existing collection instead of creating new one
- Check for slug conflicts before creation
Prevention:
- Query existing collections before creating
- Auto-generate unique slugs (e.g., append number)
- Show real-time slug availability
Example:
async function generateUniqueSlug(baseSlug: string, projectId: string) {
const collections = await api.getCollections(projectId);
const existingSlugs = collections.map(c => c.slug);
let slug = baseSlug;
let counter = 1;
while (existingSlugs.includes(slug)) {
slug = `${baseSlug}-${counter}`;
counter++;
}
return slug;
}
Duplicate Field Name
Error Message:
{
"error": "Duplicate value",
"field": "field_name",
"message": "A field with this name already exists in the collection. Please choose a different name.",
"constraint": "unique_field_name_per_collection"
}
Cause: Attempting to create field with name that already exists in collection.
Solution:
- Choose a different field name
- Update existing field instead of creating new one
- Check collection schema before adding field
Prevention:
- Fetch collection schema before modifications
- Validate field names against existing fields
- Use descriptive, unique field names
Translation Already Exists
Error Message:
Translation already exists for locale {locale}
Cause: Attempting to create translation that already exists.
Solution:
- Update existing translation instead
- Delete old translation before creating new one
- Use upsert operation if supported
Prevention:
- Check existing translations before creating
- Use update endpoints for existing translations
Example:
// Check if translation exists
const locales = await api.getContentItemLocales(itemId);
const hasTranslation = locales.available_locales.includes(targetLocale);
if (hasTranslation) {
await api.updateContentItem(itemId, targetLocale, data);
} else {
await api.createContentTranslation(itemId, targetLocale, data);
}
413 Request Entity Too Large
File or request body exceeds size limits.
File Size Limit Exceeded
Error Message:
File size exceeds 50MB limit
Cause: Uploaded file larger than 50MB maximum.
Solution:
- Compress or optimize the file
- Split large files if possible
- Use appropriate image optimization tools
Prevention:
- Validate file size on client before upload
- Show size limits in UI
- Compress images before upload
Example:
const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB
function validateFileSize(file: File) {
if (file.size > MAX_FILE_SIZE) {
throw new Error(
`File size (${(file.size / 1024 / 1024).toFixed(2)}MB) exceeds 50MB limit`
);
}
}
// Client-side image compression
async function compressImage(file: File): Promise<Blob> {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = await createImageBitmap(file);
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
return new Promise(resolve => {
canvas.toBlob(resolve, 'image/jpeg', 0.8);
});
}
422 Unprocessable Entity
Request is well-formed but semantically invalid.
Missing Required Query Parameter
Error Message:
version_name is required when include_item_counts is True
Cause: Dependent parameters not provided together.
Solution:
- Provide all required dependent parameters
- Check API documentation for parameter relationships
Prevention:
- Group related parameters in TypeScript interfaces
- Validate parameter combinations before API calls
Example:
interface ListCollectionsParams {
projectId: string;
includeItemCounts?: boolean;
versionName?: string;
}
function validateParams(params: ListCollectionsParams) {
if (params.includeItemCounts && !params.versionName) {
throw new Error('versionName required when includeItemCounts is true');
}
}
Invalid Field Schema
Error Message:
Field type 'text' cannot use interface 'single_file'
Cause: Incompatible field type and interface combination.
Solution:
- Use valid type/interface pairs:
text->input,textarea,markdownfile->single_file,multiple_filesnumber->inputboolean->input
Prevention:
- Reference field type documentation
- Validate type/interface compatibility
429 Too Many Requests
Rate limit exceeded.
Rate Limit Exceeded
Error Message:
Rate limit exceeded. Please try again later.
Cause: Too many requests in short time period.
Solution:
- Implement exponential backoff
- Wait before retrying
- Check rate limit headers
Prevention:
- Implement client-side rate limiting
- Cache frequently accessed data
- Batch operations when possible
Example:
class RateLimitedClient {
private lastRequest = 0;
private minInterval = 100; // ms between requests
async makeRequest(fn: () => Promise<any>) {
const now = Date.now();
const timeSinceLastRequest = now - this.lastRequest;
if (timeSinceLastRequest < this.minInterval) {
await new Promise(resolve =>
setTimeout(resolve, this.minInterval - timeSinceLastRequest)
);
}
this.lastRequest = Date.now();
return fn();
}
}
// Exponential backoff for retries
async function fetchWithRetry(fn: () => Promise<any>, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
return await fn();
} catch (error) {
if (error.status === 429 && i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 1s, 2s, 4s
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
throw error;
}
}
}
500 Internal Server Error
Unexpected server error.
Database Error
Error Message:
Database error: {error_details}
Cause: Unexpected database error or constraint violation.
Solution:
- Check request data for invalid values
- Retry the operation
- Contact support if error persists
Prevention:
- Validate all input data
- Handle errors gracefully with retries
- Monitor error rates
Storage Error
Error Message:
Storage error: {error_details}
Cause: File storage system unavailable or error.
Solution:
- Retry file upload
- Check network connectivity
- Verify file isn't corrupted
Prevention:
- Implement retry logic for storage operations
- Validate files before upload
- Monitor storage health
Image Processing Error
Error Message:
Image processing error: {error_details}
Cause: Failed to process or generate image variant.
Solution:
- Verify image file is valid
- Try different image format
- Check image dimensions aren't excessive
Prevention:
- Validate image files before upload
- Test with sample images
- Limit image dimensions
Database Errors
Foreign Key Violations
Error Pattern:
violates foreign key constraint
Cause: Referenced record doesn't exist (e.g., invalid project_id, collection_id).
Solution:
- Verify referenced ID exists
- Create referenced record first
- Check for typos in IDs
Prevention:
- Validate foreign keys before operations
- Use database transactions for related operations
Unique Constraint Violations
See 409 Conflict errors above for specific unique constraint violations.
Check Constraint Violations
Error Pattern:
violates check constraint
Common Causes:
- Invalid enum values
- Values outside allowed ranges
- Invalid format patterns
Solution:
- Check constraint definition
- Validate data matches allowed values
- Use proper data types
SDK-Specific Errors
VibeCMSError
The SDK wraps all errors in VibeCMSError with additional context.
Properties:
message: Human-readable error messagestatus: HTTP status code (0 for network errors)details: Additional error information
Example:
import { VibeCMSError } from '@vibecms/sdk';
try {
const items = await cms.collection('posts').all();
} catch (error) {
if (error instanceof VibeCMSError) {
console.error(`Error ${error.status}: ${error.message}`);
console.error('Details:', error.details);
// Handle specific errors
if (error.status === 404) {
// Collection not found
} else if (error.status === 401) {
// Authentication required
}
}
}
Network Errors
Error Message:
Network error occurred
Cause: Network connectivity issue or request failed.
Solution:
- Check internet connectivity
- Verify API endpoint URL
- Check for CORS issues
- Retry the request
Prevention:
- Implement offline detection
- Add retry logic with exponential backoff
- Show connectivity status to users
Example:
async function fetchWithNetworkRetry(fn: () => Promise<any>) {
const maxRetries = 3;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error instanceof VibeCMSError && error.status === 0) {
// Network error
if (attempt < maxRetries) {
const delay = attempt * 1000;
console.log(`Network error, retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
}
throw error;
}
}
}
Timeout Errors
Error Message:
Request timeout after {timeout}ms
Cause: Request took longer than configured timeout (default 30s).
Solution:
- Increase timeout for slow operations
- Check network connectivity
- Optimize query if possible
Prevention:
- Set appropriate timeouts for different operations
- Implement progress indicators for slow requests
- Paginate large data sets
Example:
const cms = createClient({
projectId: 'my-project',
baseUrl: 'https://api.vibecms.com',
timeout: 60000 // 60 second timeout for large uploads
});
Validation Errors
Field Type Mismatches
Error: Storing wrong data type in field.
Solution:
- Match data types to field definitions:
text-> stringnumber-> numberboolean-> booleanfile-> file ID string
- Validate data before submission
Example:
interface BlogPost {
title: string;
body: string;
publishDate: string; // ISO 8601 date
viewCount: number;
featured: boolean;
featuredImage: string; // File ID
}
// Type-safe content creation
const post: BlogPost = {
title: 'My Post',
body: 'Content here',
publishDate: new Date().toISOString(),
viewCount: 0,
featured: false,
featuredImage: 'file-uuid-here'
};
Required Field Violations
Error: Missing required field in content.
Solution:
- Check collection schema for required fields
- Provide all required fields in data
- Use TypeScript interfaces to enforce requirements
Prevention:
- Define interfaces matching collection schema
- Use form validation
- Show required field indicators in UI
Field Naming Violations
Error: Invalid field name format.
Rules:
- Lowercase letters, numbers, underscores only
- Must start with letter
- Cannot be reserved words
Valid Examples:
titlepublish_datefeatured_imagemeta_description
Invalid Examples:
Title(uppercase)publish-date(hyphen)1st_post(starts with number)for(reserved word)
Solution:
function toValidFieldName(name: string): string {
return name
.toLowerCase()
.replace(/[^a-z0-9_]/g, '_')
.replace(/^[0-9]/, 'n$&')
.replace(/_{2,}/g, '_');
}
// 'My Field!' -> 'my_field'
// '1st Post' -> 'n1st_post'
Best Practices
Error Handling Strategy
import { VibeCMSError } from '@vibecms/sdk';
async function robustFetch<T>(
fn: () => Promise<T>,
options = {}
): Promise<T> {
const {
maxRetries = 3,
retryDelay = 1000,
retryOn = [500, 502, 503, 504],
onError = (error: VibeCMSError) => {}
} = options;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
try {
return await fn();
} catch (error) {
if (error instanceof VibeCMSError) {
onError(error);
// Don't retry client errors
if (error.status >= 400 && error.status < 500) {
throw error;
}
// Retry server errors
if (retryOn.includes(error.status) && attempt < maxRetries) {
await new Promise(resolve =>
setTimeout(resolve, retryDelay * attempt)
);
continue;
}
}
throw error;
}
}
}
// Usage
try {
const items = await robustFetch(
() => cms.collection('posts').all(),
{
onError: (error) => console.error('Fetch error:', error.message)
}
);
} catch (error) {
// Handle final error
}
Logging and Monitoring
class ErrorLogger {
private errorCounts = new Map<number, number>();
logError(error: VibeCMSError) {
const count = this.errorCounts.get(error.status) || 0;
this.errorCounts.set(error.status, count + 1);
console.error('VibeCMS Error:', {
status: error.status,
message: error.message,
details: error.details,
timestamp: new Date().toISOString()
});
// Send to monitoring service
if (error.status >= 500) {
this.alertMonitoring(error);
}
}
getErrorStats() {
return Object.fromEntries(this.errorCounts);
}
private alertMonitoring(error: VibeCMSError) {
// Integrate with Sentry, LogRocket, etc.
}
}
const errorLogger = new ErrorLogger();
try {
await cms.collection('posts').all();
} catch (error) {
if (error instanceof VibeCMSError) {
errorLogger.logError(error);
}
throw error;
}
User-Friendly Error Messages
function getErrorMessage(error: VibeCMSError): string {
const messages: Record<number, string> = {
401: 'Please log in to continue.',
403: 'You don\'t have permission to perform this action.',
404: 'The requested content could not be found.',
409: 'This item already exists. Please choose a different name.',
413: 'The file is too large. Maximum size is 50MB.',
429: 'Too many requests. Please wait a moment and try again.',
500: 'Something went wrong. Please try again later.',
};
return messages[error.status] ||
error.message ||
'An unexpected error occurred.';
}
// Usage in UI
try {
await cms.collection('posts').all();
} catch (error) {
if (error instanceof VibeCMSError) {
toast.error(getErrorMessage(error));
}
}
Related Resources
Need Help?
If you encounter an error not covered in this guide:
- Check the GitHub Issues
- Join our Discord community
- Contact support at support@vibecms.com