Skip to content

Error Handling

The SDK provides typed errors for comprehensive error handling.

Error Types

All SDK errors extend the base DeIterateError class:

typescript
import {
  DeIterateError,
  ValidationError,
  AuthenticationError,
  AuthorizationError,
  NotFoundError,
  RateLimitError,
  ConflictError,
  InternalError,
} from '@deiterate/sdk';

Error Hierarchy

DeIterateError (base)
├── ValidationError (400)
├── AuthenticationError (401)
├── AuthorizationError (403)
├── NotFoundError (404)
├── ConflictError (409)
├── RateLimitError (429)
└── InternalError (500)

Handling Specific Errors

ValidationError

Thrown when request data is invalid:

typescript
try {
  await client.risks.create({
    name: '', // Invalid: empty name
  });
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Validation failed:', error.message);
    console.error('Field errors:', error.errors);
    // { name: ['Name is required'] }
  }
}

AuthenticationError

Thrown when authentication fails:

typescript
try {
  await client.risks.list();
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Authentication failed:', error.message);
    // "Invalid or expired API token"
    
    // Refresh token or re-authenticate
  }
}

AuthorizationError

Thrown when the user lacks permissions:

typescript
try {
  await client.users.delete('user-123');
} catch (error) {
  if (error instanceof AuthorizationError) {
    console.error('Not authorized:', error.message);
    // "Insufficient permissions for this action"
  }
}

NotFoundError

Thrown when a resource doesn't exist:

typescript
try {
  const risk = await client.risks.get('non-existent-id');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.error('Resource not found:', error.message);
    console.error('Resource type:', error.resourceType);
    console.error('Resource ID:', error.resourceId);
  }
}

RateLimitError

Thrown when rate limits are exceeded:

typescript
try {
  // Many rapid requests...
  for (let i = 0; i < 1000; i++) {
    await client.risks.list();
  }
} catch (error) {
  if (error instanceof RateLimitError) {
    console.error('Rate limited');
    console.error('Retry after:', error.retryAfter, 'seconds');
    
    // Wait and retry
    await sleep(error.retryAfter * 1000);
  }
}

ConflictError

Thrown when there's a resource conflict:

typescript
try {
  await client.users.create({
    email: 'existing@example.com', // Already exists
  });
} catch (error) {
  if (error instanceof ConflictError) {
    console.error('Conflict:', error.message);
    // "A user with this email already exists"
  }
}

InternalError

Thrown for server-side errors:

typescript
try {
  await client.risks.list();
} catch (error) {
  if (error instanceof InternalError) {
    console.error('Server error:', error.message);
    console.error('Request ID:', error.requestId);
    
    // Log for support and retry later
  }
}

Comprehensive Error Handler

Handle all error types in one place:

typescript
import { 
  DeIterateError,
  ValidationError,
  AuthenticationError,
  AuthorizationError,
  NotFoundError,
  RateLimitError,
  ConflictError,
  InternalError,
} from '@deiterate/sdk';

async function safeRequest<T>(fn: () => Promise<T>): Promise<T | null> {
  try {
    return await fn();
  } catch (error) {
    if (error instanceof ValidationError) {
      console.error('❌ Validation error:', error.message);
      return null;
    }
    
    if (error instanceof AuthenticationError) {
      console.error('🔐 Authentication required');
      // Trigger re-authentication flow
      return null;
    }
    
    if (error instanceof AuthorizationError) {
      console.error('🚫 Permission denied:', error.message);
      return null;
    }
    
    if (error instanceof NotFoundError) {
      console.error('🔍 Not found:', error.resourceId);
      return null;
    }
    
    if (error instanceof RateLimitError) {
      console.error(`⏳ Rate limited. Retry in ${error.retryAfter}s`);
      await sleep(error.retryAfter * 1000);
      return safeRequest(fn); // Retry
    }
    
    if (error instanceof ConflictError) {
      console.error('⚠️ Conflict:', error.message);
      return null;
    }
    
    if (error instanceof InternalError) {
      console.error('💥 Server error. Request ID:', error.requestId);
      return null;
    }
    
    // Unknown error
    console.error('❓ Unknown error:', error);
    throw error;
  }
}

// Usage
const risks = await safeRequest(() => client.risks.list());

Error Properties

All errors include useful properties:

typescript
interface DeIterateError {
  message: string;      // Human-readable error message
  status: number;       // HTTP status code
  code: string;         // Error code (e.g., 'VALIDATION_ERROR')
  requestId?: string;   // Request ID for support
}

Retry Strategies

Exponential Backoff

typescript
async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries = 3
): Promise<T> {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    try {
      return await fn();
    } catch (error) {
      if (error instanceof RateLimitError) {
        const delay = error.retryAfter * 1000;
        await sleep(delay);
        continue;
      }
      
      if (error instanceof InternalError && attempt < maxRetries - 1) {
        const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
        await sleep(delay);
        continue;
      }
      
      throw error;
    }
  }
  
  throw new Error('Max retries exceeded');
}

Next Steps

Released under the MIT License.