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

# Error Handling

> Catch typed SDK errors like ValidationError, RateLimitError, and AuthenticationError with recovery hints, retry logic, and structured logging for production.

You can handle errors gracefully using the SDK's structured error classes. All errors extend the base `WebacyError` class, making it easy to catch and respond to different failure scenarios.

## Error Classes

```typescript theme={null}
import {
  WebacyError,
  AuthenticationError,
  ValidationError,
  RateLimitError,
  NotFoundError,
  NetworkError,
} from '@webacy-xyz/sdk';
```

| Error                 | HTTP Status | Description                 |
| --------------------- | ----------- | --------------------------- |
| `AuthenticationError` | 401         | Invalid or missing API key  |
| `ValidationError`     | 400         | Invalid input parameters    |
| `RateLimitError`      | 429         | Rate limit exceeded         |
| `NotFoundError`       | 404         | Resource not found          |
| `NetworkError`        | -           | Network connectivity issues |
| `WebacyError`         | Various     | Base class for all errors   |

## Basic Error Handling

```typescript theme={null}
import { ThreatClient, ValidationError, RateLimitError } from '@webacy-xyz/sdk';

const client = new ThreatClient({ apiKey: process.env.WEBACY_API_KEY });

try {
  const result = await client.addresses.analyze('0x...');
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Invalid input:', error.message);
  } else if (error instanceof RateLimitError) {
    console.error('Rate limited, retry later');
  } else {
    console.error('Unexpected error:', error);
  }
}
```

***

## Error Properties

All SDK errors include these properties:

```typescript theme={null}
try {
  await client.addresses.analyze('invalid-address');
} catch (error) {
  if (error instanceof WebacyError) {
    console.log(error.message);    // Human-readable message
    console.log(error.code);       // Programmatic error code
    console.log(error.status);     // HTTP status code (if applicable)
    console.log(error.requestId);  // Request ID for support
    console.log(error.endpoint);   // Failed endpoint
    console.log(error.cause);      // Original error (if wrapped)
  }
}
```

### Recovery Suggestions

Errors provide recovery suggestions:

```typescript theme={null}
try {
  await client.addresses.analyze('invalid');
} catch (error) {
  if (error instanceof WebacyError) {
    const suggestion = error.getRecoverySuggestion();
    if (suggestion) {
      console.log('How to fix:', suggestion);
    }
  }
}
```

***

## Specific Error Types

### AuthenticationError

Thrown when the API key is invalid or missing.

```typescript theme={null}
import { ThreatClient, AuthenticationError } from '@webacy-xyz/sdk';

try {
  const client = new ThreatClient({ apiKey: 'invalid-key' });
  await client.addresses.analyze('0x...');
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.error('Invalid API key');
    // Prompt user to check their API key
  }
}
```

### ValidationError

Thrown when input parameters are invalid.

```typescript theme={null}
import { ThreatClient, Chain, ValidationError } from '@webacy-xyz/sdk';

const client = new ThreatClient({ apiKey: 'your-api-key' });

try {
  await client.addresses.analyze('not-an-address', { chain: Chain.ETH });
} catch (error) {
  if (error instanceof ValidationError) {
    console.error('Validation failed:', error.message);
    console.log('Fix:', error.getRecoverySuggestion());
    // "Ensure the address is a valid Ethereum address (0x followed by 40 hex characters)"
  }
}
```

### RateLimitError

Thrown when you exceed API rate limits. The SDK automatically retries rate-limited requests with exponential backoff.

```typescript theme={null}
import { ThreatClient, RateLimitError } from '@webacy-xyz/sdk';

const client = new ThreatClient({ apiKey: 'your-api-key' });

try {
  await client.addresses.analyze('0x...');
} catch (error) {
  if (error instanceof RateLimitError) {
    console.error('Rate limited');

    if (error.retryAfter) {
      console.log(`Retry after ${error.retryAfter} seconds`);
    }

    if (error.resetAt) {
      const resetDate = new Date(error.resetAt * 1000);
      console.log(`Limit resets at ${resetDate.toISOString()}`);
    }
  }
}
```

### NotFoundError

Thrown when the requested resource doesn't exist.

```typescript theme={null}
import { ThreatClient, NotFoundError } from '@webacy-xyz/sdk';

const client = new ThreatClient({ apiKey: 'your-api-key' });

try {
  await client.contracts.analyze('0xNonExistentContract...');
} catch (error) {
  if (error instanceof NotFoundError) {
    console.error('Contract not found');
  }
}
```

### NetworkError

Thrown when there are network connectivity issues.

```typescript theme={null}
import { ThreatClient, NetworkError } from '@webacy-xyz/sdk';

const client = new ThreatClient({ apiKey: 'your-api-key' });

try {
  await client.addresses.analyze('0x...');
} catch (error) {
  if (error instanceof NetworkError) {
    console.error('Network error:', error.message);
    if (error.requestId) {
      console.error('Request ID:', error.requestId);
    }
    // Retry or notify user of connectivity issues
  }
}
```

***

## Retryable Errors

Check if an error can be retried:

```typescript theme={null}
import { ThreatClient, WebacyError } from '@webacy-xyz/sdk';

const client = new ThreatClient({ apiKey: 'your-api-key' });

async function analyzeWithRetry(address: string, maxRetries = 3) {
  for (let attempt = 1; attempt <= maxRetries; attempt++) {
    try {
      return await client.addresses.analyze(address);
    } catch (error) {
      if (error instanceof WebacyError && error.isRetryable() && attempt < maxRetries) {
        // Safe to retry (rate limits, network issues)
        const delay = Math.pow(2, attempt) * 1000; // Exponential backoff
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      // Don't retry (validation errors, auth errors) or max retries reached
      throw error;
    }
  }
}
```

Retryable errors:

* `RateLimitError` - Wait and retry
* `NetworkError` - Retry after connectivity restored

Non-retryable errors:

* `AuthenticationError` - Fix API key
* `ValidationError` - Fix input
* `NotFoundError` - Resource doesn't exist

***

## Automatic Retries

The SDK automatically retries failed requests with exponential backoff:

```typescript theme={null}
const client = new ThreatClient({
  apiKey: process.env.WEBACY_API_KEY,
  retry: {
    maxRetries: 3,         // Default: 3
    initialDelay: 1000,    // Default: 1000ms
    maxDelay: 30000,       // Default: 30000ms
    backoffMultiplier: 2,  // Default: 2
  },
});
```

Disable retries for specific requests using an AbortController:

```typescript theme={null}
const controller = new AbortController();

// Timeout after 5 seconds (no retries)
setTimeout(() => controller.abort(), 5000);

try {
  await client.addresses.analyze('0x...', {
    signal: controller.signal,
  });
} catch (error) {
  if (error.name === 'AbortError') {
    console.log('Request timed out');
  }
}
```

***

## Logging Errors

Convert errors to JSON for logging:

```typescript theme={null}
try {
  await client.addresses.analyze('0x...');
} catch (error) {
  if (error instanceof WebacyError) {
    // Structured logging
    console.error(JSON.stringify(error.toJSON(), null, 2));
    // {
    //   "name": "ValidationError",
    //   "message": "Invalid Ethereum address format",
    //   "code": "VALIDATION_ERROR",
    //   "status": 400,
    //   "requestId": "req_abc123",
    //   "endpoint": "/addresses/invalid"
    // }
  }
}
```

***

## Full Example

```typescript theme={null}
import {
  ThreatClient,
  Chain,
  WebacyError,
  AuthenticationError,
  ValidationError,
  RateLimitError,
  NotFoundError,
  NetworkError,
} from '@webacy-xyz/sdk';

async function safeAnalyze(client: ThreatClient, address: string) {
  try {
    return await client.addresses.analyze(address);

  } catch (error) {
    // Handle specific error types
    if (error instanceof AuthenticationError) {
      throw new Error('Please check your API key configuration');
    }

    if (error instanceof ValidationError) {
      throw new Error(`Invalid address: ${error.getRecoverySuggestion()}`);
    }

    if (error instanceof RateLimitError) {
      const waitTime = error.retryAfter || 60;
      throw new Error(`Too many requests. Please wait ${waitTime} seconds.`);
    }

    if (error instanceof NotFoundError) {
      return null; // Address not found, return null
    }

    if (error instanceof NetworkError) {
      throw new Error('Network error. Please check your connection.');
    }

    // Log unexpected errors with request ID for support
    if (error instanceof WebacyError) {
      console.error('Unexpected error:', error.toJSON());
      if (error.requestId) {
        console.error(`Request ID for support: ${error.requestId}`);
      }
    }

    throw error;
  }
}
```
