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

# Error Handling

> Handle errors gracefully in your ZKScore integration

## Overview

The ZKScore SDK provides detailed error information to help you build robust applications. This guide covers error types, handling strategies, and best practices.

## Error Structure

All SDK errors follow a consistent structure:

```typescript theme={null}
{
  code: 'ERROR_CODE',
  message: 'Human-readable error message',
  details: {
    // Additional context
  },
  statusCode: 400,
  timestamp: '2024-10-23T14:30:00Z'
}
```

## Common Error Codes

### Authentication Errors

<AccordionGroup>
  <Accordion title="INVALID_API_KEY">
    **Code**: `INVALID_API_KEY`\
    **Status**: 401

    The provided API key is invalid or has been revoked.

    ```typescript theme={null}
    try {
      await sdk.scores.getScore(address);
    } catch (error) {
      if (error.code === 'INVALID_API_KEY') {
        console.log('Please check your API key');
        // Redirect to API key management
      }
    }
    ```
  </Accordion>

  <Accordion title="API_KEY_EXPIRED">
    **Code**: `API_KEY_EXPIRED`\
    **Status**: 401

    The API key has expired and needs to be renewed.
  </Accordion>

  <Accordion title="UNAUTHORIZED">
    **Code**: `UNAUTHORIZED`\
    **Status**: 403

    The API key doesn't have permission for this operation.
  </Accordion>
</AccordionGroup>

### Resource Errors

<AccordionGroup>
  <Accordion title="IDENTITY_NOT_FOUND">
    **Code**: `IDENTITY_NOT_FOUND`\
    **Status**: 404

    The address doesn't have a ZKScore identity.

    ```typescript theme={null}
    try {
      const identity = await sdk.identity.getIdentity(address);
    } catch (error) {
      if (error.code === 'IDENTITY_NOT_FOUND') {
        // Prompt user to create identity
        const newIdentity = await sdk.identity.mint({
          address,
          username: 'alice',
        });
      }
    }
    ```
  </Accordion>

  <Accordion title="USERNAME_TAKEN">
    **Code**: `USERNAME_TAKEN`\
    **Status**: 409

    The requested username is already in use.
  </Accordion>

  <Accordion title="ACHIEVEMENT_NOT_FOUND">
    **Code**: `ACHIEVEMENT_NOT_FOUND`\
    **Status**: 404

    The specified achievement doesn't exist.
  </Accordion>

  <Accordion title="ATTESTATION_NOT_FOUND">
    **Code**: `ATTESTATION_NOT_FOUND`\
    **Status**: 404

    The attestation ID doesn't exist or has been revoked.
  </Accordion>
</AccordionGroup>

### Rate Limiting

<AccordionGroup>
  <Accordion title="RATE_LIMIT_EXCEEDED">
    **Code**: `RATE_LIMIT_EXCEEDED`\
    **Status**: 429

    Too many requests. Includes retry-after information.

    ```typescript theme={null}
    try {
      await sdk.scores.getScore(address);
    } catch (error) {
      if (error.code === 'RATE_LIMIT_EXCEEDED') {
        const retryAfter = error.details.retryAfter; // seconds
        console.log(`Rate limited. Retry after ${retryAfter}s`);
        
        // Wait and retry
        await new Promise(resolve => setTimeout(resolve, retryAfter * 1000));
        return sdk.scores.getScore(address);
      }
    }
    ```
  </Accordion>
</AccordionGroup>

### Validation Errors

<AccordionGroup>
  <Accordion title="INVALID_ADDRESS">
    **Code**: `INVALID_ADDRESS`\
    **Status**: 400

    The provided Ethereum address is invalid.
  </Accordion>

  <Accordion title="INVALID_PARAMETERS">
    **Code**: `INVALID_PARAMETERS`\
    **Status**: 400

    Request parameters are invalid or missing.

    ```typescript theme={null}
    try {
      await sdk.achievements.claim({
        achievementId: '',  // Invalid
      });
    } catch (error) {
      if (error.code === 'INVALID_PARAMETERS') {
        console.log('Validation errors:', error.details.errors);
        // {
        //   achievementId: 'Required field',
        //   proof: 'Invalid proof format'
        // }
      }
    }
    ```
  </Accordion>

  <Accordion title="INVALID_SCHEMA">
    **Code**: `INVALID_SCHEMA`\
    **Status**: 400

    Attestation schema is invalid or doesn't exist.
  </Accordion>
</AccordionGroup>

### Network Errors

<AccordionGroup>
  <Accordion title="NETWORK_ERROR">
    **Code**: `NETWORK_ERROR`\
    **Status**: 500

    Network connection failed.
  </Accordion>

  <Accordion title="TIMEOUT">
    **Code**: `TIMEOUT`\
    **Status**: 504

    Request timed out.
  </Accordion>

  <Accordion title="SERVICE_UNAVAILABLE">
    **Code**: `SERVICE_UNAVAILABLE`\
    **Status**: 503

    The service is temporarily unavailable.
  </Accordion>
</AccordionGroup>

### Data Errors

<AccordionGroup>
  <Accordion title="INSUFFICIENT_DATA">
    **Code**: `INSUFFICIENT_DATA`\
    **Status**: 400

    Not enough data to complete the operation.

    ```typescript theme={null}
    try {
      const proof = await sdk.zkProofs.generate({
        address,
        type: 'trading-volume',
        minVolume: '100000',
      });
    } catch (error) {
      if (error.code === 'INSUFFICIENT_DATA') {
        console.log('User needs more trading history');
      }
    }
    ```
  </Accordion>

  <Accordion title="SCORE_NOT_READY">
    **Code**: `SCORE_NOT_READY`\
    **Status**: 202

    Score is still being calculated.
  </Accordion>
</AccordionGroup>

## Error Handling Patterns

### Basic Try-Catch

```typescript theme={null}
async function getScoreSafely(address: string) {
  try {
    const score = await sdk.scores.getScore(address);
    return { success: true, data: score };
  } catch (error) {
    return {
      success: false,
      error: {
        code: error.code,
        message: error.message,
      },
    };
  }
}
```

### Specific Error Handling

```typescript theme={null}
async function mintIdentityWithErrorHandling(address: string, username: string) {
  try {
    const identity = await sdk.identity.mint({ address, username });
    return { success: true, identity };
  } catch (error) {
    switch (error.code) {
      case 'USERNAME_TAKEN':
        return {
          success: false,
          error: 'This username is already taken. Please choose another.',
        };
      
      case 'INVALID_ADDRESS':
        return {
          success: false,
          error: 'Invalid Ethereum address provided.',
        };
      
      case 'IDENTITY_ALREADY_EXISTS':
        return {
          success: false,
          error: 'This address already has an identity.',
        };
      
      case 'RATE_LIMIT_EXCEEDED':
        return {
          success: false,
          error: 'Too many requests. Please try again later.',
        };
      
      default:
        return {
          success: false,
          error: 'An unexpected error occurred. Please try again.',
        };
    }
  }
}
```

### Retry Logic

```typescript theme={null}
async function withRetry<T>(
  fn: () => Promise<T>,
  maxRetries: number = 3,
  delayMs: number = 1000
): Promise<T> {
  let lastError: any;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      // Don't retry on certain errors
      if (['INVALID_API_KEY', 'INVALID_ADDRESS', 'USERNAME_TAKEN'].includes(error.code)) {
        throw error;
      }
      
      // Exponential backoff
      const delay = delayMs * Math.pow(2, i);
      console.log(`Retry ${i + 1}/${maxRetries} after ${delay}ms`);
      await new Promise(resolve => setTimeout(resolve, delay));
    }
  }
  
  throw lastError;
}

// Usage
const score = await withRetry(() => sdk.scores.getScore(address));
```

### Rate Limit Handler

```typescript theme={null}
class RateLimitHandler {
  private queue: Array<() => Promise<any>> = [];
  private processing = false;
  private lastRequest = 0;
  private minInterval = 1000; // 1 second between requests

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    return new Promise((resolve, reject) => {
      this.queue.push(async () => {
        try {
          const result = await fn();
          resolve(result);
        } catch (error) {
          if (error.code === 'RATE_LIMIT_EXCEEDED') {
            // Re-queue the request
            this.queue.unshift(fn);
            const retryAfter = error.details.retryAfter * 1000;
            await new Promise(r => setTimeout(r, retryAfter));
          } else {
            reject(error);
          }
        }
      });

      if (!this.processing) {
        this.processQueue();
      }
    });
  }

  private async processQueue() {
    this.processing = true;

    while (this.queue.length > 0) {
      const now = Date.now();
      const timeSinceLastRequest = now - this.lastRequest;

      if (timeSinceLastRequest < this.minInterval) {
        await new Promise(resolve =>
          setTimeout(resolve, this.minInterval - timeSinceLastRequest)
        );
      }

      const fn = this.queue.shift();
      if (fn) {
        this.lastRequest = Date.now();
        await fn();
      }
    }

    this.processing = false;
  }
}

const rateLimiter = new RateLimitHandler();

// Usage
const score = await rateLimiter.execute(() => sdk.scores.getScore(address));
```

### Circuit Breaker

```typescript theme={null}
class CircuitBreaker {
  private failureCount = 0;
  private successCount = 0;
  private lastFailureTime = 0;
  private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';

  constructor(
    private threshold: number = 5,
    private timeout: number = 60000
  ) {}

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'OPEN') {
      if (Date.now() - this.lastFailureTime > this.timeout) {
        this.state = 'HALF_OPEN';
      } else {
        throw new Error('Circuit breaker is OPEN');
      }
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onSuccess() {
    this.failureCount = 0;
    this.successCount++;

    if (this.state === 'HALF_OPEN' && this.successCount >= 2) {
      this.state = 'CLOSED';
      this.successCount = 0;
    }
  }

  private onFailure() {
    this.failureCount++;
    this.lastFailureTime = Date.now();

    if (this.failureCount >= this.threshold) {
      this.state = 'OPEN';
    }
  }
}

const breaker = new CircuitBreaker();

// Usage
const score = await breaker.execute(() => sdk.scores.getScore(address));
```

### Error Logging

```typescript theme={null}
class ErrorLogger {
  async logError(error: any, context: any = {}) {
    const errorLog = {
      code: error.code,
      message: error.message,
      statusCode: error.statusCode,
      timestamp: new Date().toISOString(),
      context,
      stack: error.stack,
    };

    // Send to logging service
    console.error('ZKScore Error:', errorLog);

    // Could also send to external service
    // await sendToSentry(errorLog);
  }
}

const logger = new ErrorLogger();

// Usage
try {
  await sdk.scores.getScore(address);
} catch (error) {
  await logger.logError(error, {
    operation: 'getScore',
    address,
    userId: currentUser.id,
  });
  throw error;
}
```

### Graceful Degradation

```typescript theme={null}
async function getScoreWithFallback(address: string) {
  try {
    return await sdk.scores.getScore(address);
  } catch (error) {
    if (error.code === 'SCORE_NOT_READY') {
      // Return estimated score
      return {
        overall: 500,
        breakdown: {},
        estimated: true,
        message: 'Score is being calculated',
      };
    }

    if (error.code === 'SERVICE_UNAVAILABLE') {
      // Return cached score if available
      const cached = await getCachedScore(address);
      if (cached) {
        return {
          ...cached,
          cached: true,
          message: 'Showing cached score',
        };
      }
    }

    // Re-throw if no fallback available
    throw error;
  }
}
```

## Error Boundaries (React)

```tsx theme={null}
import { Component, ReactNode } from 'react';

interface Props {
  children: ReactNode;
  fallback?: ReactNode;
}

interface State {
  hasError: boolean;
  error?: any;
}

class ZKScoreErrorBoundary extends Component<Props, State> {
  constructor(props: Props) {
    super(props);
    this.state = { hasError: false };
  }

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

  componentDidCatch(error: any, errorInfo: any) {
    console.error('ZKScore Error:', error, errorInfo);
    
    // Log to error tracking service
    // logErrorToService(error, errorInfo);
  }

  render() {
    if (this.state.hasError) {
      return this.props.fallback || (
        <div className="error-container">
          <h2>Something went wrong</h2>
          <p>{this.state.error?.message}</p>
          <button onClick={() => this.setState({ hasError: false })}>
            Try Again
          </button>
        </div>
      );
    }

    return this.props.children;
  }
}

// Usage
<ZKScoreErrorBoundary>
  <Dashboard />
</ZKScoreErrorBoundary>
```

## Validation Helper

```typescript theme={null}
function validateAddress(address: string): void {
  if (!address || typeof address !== 'string') {
    throw {
      code: 'INVALID_ADDRESS',
      message: 'Address is required',
      statusCode: 400,
    };
  }

  if (!/^0x[a-fA-F0-9]{40}$/.test(address)) {
    throw {
      code: 'INVALID_ADDRESS',
      message: 'Invalid Ethereum address format',
      statusCode: 400,
    };
  }
}

// Usage
try {
  validateAddress(userInput);
  const score = await sdk.scores.getScore(userInput);
} catch (error) {
  // Handle validation error
}
```

## TypeScript Error Types

```typescript theme={null}
interface ZKScoreError {
  code: string;
  message: string;
  details?: Record<string, any>;
  statusCode: number;
  timestamp: string;
}

type ErrorCode =
  | 'INVALID_API_KEY'
  | 'API_KEY_EXPIRED'
  | 'UNAUTHORIZED'
  | 'IDENTITY_NOT_FOUND'
  | 'USERNAME_TAKEN'
  | 'RATE_LIMIT_EXCEEDED'
  | 'INVALID_ADDRESS'
  | 'INVALID_PARAMETERS'
  | 'NETWORK_ERROR'
  | 'TIMEOUT'
  | 'SERVICE_UNAVAILABLE'
  | 'INSUFFICIENT_DATA'
  | 'SCORE_NOT_READY';
```

## Best Practices

<CardGroup cols={2}>
  <Card title="Always Handle Errors" icon="shield-check">
    Wrap all SDK calls in try-catch blocks
  </Card>

  <Card title="Provide Feedback" icon="message">
    Show user-friendly error messages
  </Card>

  <Card title="Log Errors" icon="file-text">
    Track errors for debugging and monitoring
  </Card>

  <Card title="Implement Retries" icon="rotate">
    Retry transient failures with backoff
  </Card>

  <Card title="Validate Input" icon="check-circle">
    Validate before making API calls
  </Card>

  <Card title="Graceful Degradation" icon="trending-down">
    Provide fallbacks when possible
  </Card>
</CardGroup>

## Next Steps

<CardGroup cols={2}>
  <Card title="Rate Limiting Guide" icon="gauge" href="/guides/rate-limiting">
    Learn about rate limits
  </Card>

  <Card title="Best Practices" icon="star" href="/guides/best-practices">
    SDK best practices
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Complete API documentation
  </Card>

  <Card title="Error Codes Reference" icon="list" href="/reference/error-codes">
    All error codes
  </Card>
</CardGroup>
