> ## Documentation Index
> Fetch the complete documentation index at: https://twigz.arqqin.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Error Handling

> Comprehensive error handling patterns and best practices

## Overview

The Twigz API provides comprehensive error handling with structured error responses, proper HTTP status codes, and detailed error information to help developers build robust applications.

## Error Response Format

All API errors follow a consistent structure:

```json theme={null}
{
  "error": "Human-readable error message",
  "code": "MACHINE_READABLE_ERROR_CODE",
  "details": {
    "field": "specific field errors",
    "context": "additional context"
  },
  "timestamp": 1640995200000,
  "requestId": "req_123456789"
}
```

## HTTP Status Codes

<CardGroup cols={2}>
  <Card title="2xx Success" icon="check-circle" color="#10b981">
    **200** - OK\
    **201** - Created\
    **204** - No Content
  </Card>

  <Card title="4xx Client Errors" icon="exclamation-triangle" color="#f59e0b">
    **400** - Bad Request\
    **401** - Unauthorized\
    **403** - Forbidden\
    **404** - Not Found\
    **409** - Conflict
  </Card>

  <Card title="5xx Server Errors" icon="x-circle" color="#ef4444">
    **500** - Internal Server Error\
    **502** - Bad Gateway\
    **503** - Service Unavailable
  </Card>

  <Card title="Payment Errors" icon="credit-card" color="#8b5cf6">
    **402** - Payment Required\
    **422** - Payment Failed\
    **424** - Payment Processing
  </Card>
</CardGroup>

## Common Error Types

### Authentication Errors

<AccordionGroup>
  <Accordion title="401 - Authentication Required">
    **When**: User is not authenticated

    ```json theme={null}
    {
      "error": "Authentication required",
      "code": "UNAUTHENTICATED",
      "message": "Please sign in to access this resource"
    }
    ```

    **Solution**: Redirect to login page or show authentication prompt
  </Accordion>

  <Accordion title="403 - Insufficient Permissions">
    **When**: User lacks required permissions

    ```json theme={null}
    {
      "error": "Insufficient permissions",
      "code": "FORBIDDEN", 
      "details": {
        "requiredRole": "store_owner",
        "userRole": "customer",
        "resource": "store_management"
      }
    }
    ```

    **Solution**: Show access denied message or upgrade account prompt
  </Accordion>
</AccordionGroup>

### Validation Errors

<AccordionGroup>
  <Accordion title="400 - Validation Failed">
    **When**: Request data is invalid

    ```json theme={null}
    {
      "error": "Validation failed",
      "code": "VALIDATION_ERROR",
      "details": {
        "name": "Product name must be between 1 and 100 characters",
        "price": "Price must be a positive number",
        "category": "Category ID is required"
      }
    }
    ```

    **Solution**: Show field-specific error messages to user
  </Accordion>

  <Accordion title="409 - Resource Conflict">
    **When**: Resource already exists or conflicts

    ```json theme={null}
    {
      "error": "Store already exists for this user",
      "code": "RESOURCE_CONFLICT",
      "details": {
        "existingStoreId": "j123456789",
        "storeName": "Mario's Pizza"
      }
    }
    ```

    **Solution**: Redirect to existing resource or offer update option
  </Accordion>
</AccordionGroup>

### Business Logic Errors

<AccordionGroup>
  <Accordion title="400 - Insufficient Stock">
    **When**: Not enough inventory for order

    ```json theme={null}
    {
      "error": "Insufficient stock for one or more items",
      "code": "INSUFFICIENT_STOCK",
      "details": {
        "productId": "p123456789",
        "productName": "Pizza Margherita",
        "requested": 5,
        "available": 3
      }
    }
    ```

    **Solution**: Update cart quantities or remove unavailable items
  </Accordion>

  <Accordion title="400 - Store Inactive">
    **When**: Store is not accepting orders

    ```json theme={null}
    {
      "error": "Store is not accepting orders",
      "code": "STORE_INACTIVE",
      "details": {
        "storeId": "j123456789",
        "storeName": "Mario's Pizza",
        "status": "temporarily_closed",
        "reason": "Store is temporarily closed for maintenance"
      }
    }
    ```

    **Solution**: Show store status and suggest alternatives
  </Accordion>
</AccordionGroup>

### Payment Errors

<AccordionGroup>
  <Accordion title="402 - Payment Declined">
    **When**: Payment method declined

    ```json theme={null}
    {
      "error": "Payment declined",
      "code": "PAYMENT_DECLINED",
      "details": {
        "declineCode": "insufficient_funds",
        "paymentMethod": "card",
        "lastFour": "4242"
      }
    }
    ```

    **Solution**: Ask user to try different payment method
  </Accordion>

  <Accordion title="422 - Payment Processing Failed">
    **When**: Payment processing error

    ```json theme={null}
    {
      "error": "Payment processing failed",
      "code": "PAYMENT_PROCESSING_ERROR",
      "details": {
        "paymentIntentId": "pi_123456789",
        "stripeError": "Your card was declined",
        "retryable": true
      }
    }
    ```

    **Solution**: Allow retry or suggest alternative payment method
  </Accordion>
</AccordionGroup>

## Error Handling Patterns

### React Error Boundary

<CodeGroup>
  ```typescript Error Boundary theme={null}
  import React from 'react';
  import { ConvexError } from 'convex/values';

  interface ErrorBoundaryState {
    hasError: boolean;
    error: Error | null;
  }

  class ApiErrorBoundary extends React.Component<
    React.PropsWithChildren<{}>,
    ErrorBoundaryState
  > {
    constructor(props: React.PropsWithChildren<{}>) {
      super(props);
      this.state = { hasError: false, error: null };
    }

    static getDerivedStateFromError(error: Error): ErrorBoundaryState {
      return { hasError: true, error };
    }

    componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
      console.error('API Error caught by boundary:', error, errorInfo);
      
      // Report to error tracking service
      // reportError(error, errorInfo);
    }

    render() {
      if (this.state.hasError) {
        const error = this.state.error;
        
        if (error instanceof ConvexError) {
          // Handle Convex-specific errors
          return (
            <div className="bg-red-50 p-4 rounded-lg">
              <h2 className="text-lg font-semibold text-red-900 mb-2">
                Something went wrong
              </h2>
              <p className="text-red-700">{error.data}</p>
              <button 
                onClick={() => this.setState({ hasError: false, error: null })}
                className="mt-2 px-4 py-2 bg-red-600 text-white rounded hover:bg-red-700"
              >
                Try Again
              </button>
            </div>
          );
        }
        
        // Handle other errors
        return (
          <div className="bg-gray-50 p-4 rounded-lg">
            <h2 className="text-lg font-semibold text-gray-900 mb-2">
              Unexpected Error
            </h2>
            <p className="text-gray-700">
              An unexpected error occurred. Please refresh the page or contact support.
            </p>
            <button 
              onClick={() => window.location.reload()}
              className="mt-2 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
            >
              Refresh Page
            </button>
          </div>
        );
      }

      return this.props.children;
    }
  }

  export default ApiErrorBoundary;
  ```

  ```typescript Error Hook theme={null}
  import { useState } from 'react';
  import { ConvexError } from 'convex/values';

  interface UseErrorHandlerReturn {
    error: string | null;
    showError: (error: any) => void;
    clearError: () => void;
    handleAsync: <T>(asyncFn: () => Promise<T>) => Promise<T | null>;
  }

  function useErrorHandler(): UseErrorHandlerReturn {
    const [error, setError] = useState<string | null>(null);

    const showError = (error: any) => {
      if (error instanceof ConvexError) {
        setError(error.data);
      } else if (error instanceof Error) {
        setError(error.message);
      } else if (typeof error === 'string') {
        setError(error);
      } else {
        setError('An unexpected error occurred');
      }
    };

    const clearError = () => setError(null);

    const handleAsync = async <T,>(asyncFn: () => Promise<T>): Promise<T | null> => {
      try {
        clearError();
        return await asyncFn();
      } catch (error) {
        showError(error);
        return null;
      }
    };

    return { error, showError, clearError, handleAsync };
  }

  // Usage
  function MyComponent() {
    const { error, showError, clearError, handleAsync } = useErrorHandler();
    
    const handleCreateProduct = async () => {
      const result = await handleAsync(async () => {
        return await convex.mutation(api.shared.products.createProduct, {
          name: "New Product",
          // ... other fields
        });
      });
      
      if (result) {
        console.log('Product created:', result);
      }
      // Error is automatically handled and displayed
    };

    return (
      <div>
        {error && (
          <div className="bg-red-50 p-4 rounded-lg mb-4">
            <p className="text-red-700">{error}</p>
            <button onClick={clearError} className="text-red-600 underline">
              Dismiss
            </button>
          </div>
        )}
        
        <button onClick={handleCreateProduct}>
          Create Product
        </button>
      </div>
    );
  }
  ```
</CodeGroup>

## Retry Mechanisms

<CodeGroup>
  ```typescript Automatic Retry theme={null}
  async function withRetry<T>(
    fn: () => Promise<T>,
    maxRetries: number = 3,
    delay: number = 1000
  ): Promise<T> {
    let lastError: any;
    
    for (let attempt = 1; attempt <= maxRetries; attempt++) {
      try {
        return await fn();
      } catch (error) {
        lastError = error;
        
        // Don't retry on client errors (4xx)
        if (error instanceof ConvexError) {
          const errorData = error.data;
          if (typeof errorData === 'object' && errorData.status >= 400 && errorData.status < 500) {
            throw error;
          }
        }
        
        if (attempt === maxRetries) {
          throw lastError;
        }
        
        // Exponential backoff
        await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, attempt - 1)));
      }
    }
    
    throw lastError;
  }

  // Usage
  const result = await withRetry(async () => {
    return await convex.mutation(api.shared.products.createProduct, productData);
  }, 3, 1000);
  ```

  ```typescript Network Error Handling theme={null}
  function useNetworkErrorHandler() {
    const [isOnline, setIsOnline] = useState(navigator.onLine);
    const [retryQueue, setRetryQueue] = useState<Array<() => Promise<any>>>([]);

    useEffect(() => {
      const handleOnline = () => {
        setIsOnline(true);
        // Process retry queue when back online
        retryQueue.forEach(fn => fn());
        setRetryQueue([]);
      };

      const handleOffline = () => setIsOnline(false);

      window.addEventListener('online', handleOnline);
      window.addEventListener('offline', handleOffline);

      return () => {
        window.removeEventListener('online', handleOnline);
        window.removeEventListener('offline', handleOffline);
      };
    }, [retryQueue]);

    const handleNetworkError = (retryFn: () => Promise<any>) => {
      if (!isOnline) {
        setRetryQueue(prev => [...prev, retryFn]);
        return 'You are offline. Changes will be saved when connection is restored.';
      }
      return 'Network error. Please try again.';
    };

    return { isOnline, handleNetworkError };
  }
  ```
</CodeGroup>

<Note>
  The API uses Convex's built-in error handling which provides structured errors with proper typing and automatic retry mechanisms for transient failures.
</Note>

<Tip>
  Always handle errors gracefully in your UI and provide clear feedback to users about what went wrong and how to resolve it.
</Tip>
