Copied to clipboard!

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:

  1. Verify the file exists and contains data before uploading
  2. Check file path is correct
  3. 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:

  1. Use only allowed status values: draft, published, or archived
  2. 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:

  1. Check API documentation for required parameters
  2. Ensure all required fields are provided
  3. 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:

  1. Check available locales: GET /api/locales?project_id={project_id}
  2. Create the locale if needed
  3. 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:

  1. Check file MIME type before requesting variants
  2. 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:

  1. Include JWT token in Authorization header: Bearer {token}
  2. Or include API key in requests
  3. 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:

  1. Verify token is properly formatted (3 base64 segments separated by dots)
  2. Ensure token contains required fields (sub, email)
  3. 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:

  1. Refresh the token using refresh token flow
  2. Re-authenticate user if refresh fails
  3. 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:

  1. Request a new upload token
  2. Check token expiration (typically 1 hour)
  3. 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:

  1. Check user's account membership and role
  2. Request elevated permissions from account owner
  3. 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:

  1. Verify user is member of the account
  2. Check project belongs to user's account
  3. 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:

  1. Verify collection exists in project
  2. Check collection slug spelling
  3. 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:

  1. Verify item ID is correct
  2. Check if item exists for other locales
  3. 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:

  1. Verify file ID is correct
  2. Check if file was deleted
  3. 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:

  1. Publish the content item in CMS admin
  2. Use authenticated API for draft content
  3. 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:

  1. Choose a different slug
  2. Update existing collection instead of creating new one
  3. 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:

  1. Choose a different field name
  2. Update existing field instead of creating new one
  3. 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:

  1. Update existing translation instead
  2. Delete old translation before creating new one
  3. 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:

  1. Compress or optimize the file
  2. Split large files if possible
  3. 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:

  1. Provide all required dependent parameters
  2. 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:

  1. Use valid type/interface pairs:
    • text -> input, textarea, markdown
    • file -> single_file, multiple_files
    • number -> input
    • boolean -> 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:

  1. Implement exponential backoff
  2. Wait before retrying
  3. 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:

  1. Check request data for invalid values
  2. Retry the operation
  3. 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:

  1. Retry file upload
  2. Check network connectivity
  3. 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:

  1. Verify image file is valid
  2. Try different image format
  3. 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:

  1. Verify referenced ID exists
  2. Create referenced record first
  3. 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:

  1. Check constraint definition
  2. Validate data matches allowed values
  3. Use proper data types

SDK-Specific Errors

VibeCMSError

The SDK wraps all errors in VibeCMSError with additional context.

Properties:

  • message: Human-readable error message
  • status: 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:

  1. Check internet connectivity
  2. Verify API endpoint URL
  3. Check for CORS issues
  4. 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:

  1. Increase timeout for slow operations
  2. Check network connectivity
  3. 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:

  1. Match data types to field definitions:
    • text -> string
    • number -> number
    • boolean -> boolean
    • file -> file ID string
  2. 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:

  1. Check collection schema for required fields
  2. Provide all required fields in data
  3. 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:

  • title
  • publish_date
  • featured_image
  • meta_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:

  1. Check the GitHub Issues
  2. Join our Discord community
  3. Contact support at support@vibecms.com