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

# Error Handling

> Understanding and handling API errors

Zentra uses conventional HTTP response codes and returns detailed error information to help you debug issues quickly.

## Error Response Format

All errors follow this structure:

```json theme={null}
{
  "success": false,
  "error": {
    "code": "validation_error",
    "message": "Invalid email address provided",
    "field": "email",
    "status": 400,
    "details": {
      "reason": "Email format is incorrect"
    }
  }
}
```

### Error Object

| Field     | Type   | Description                                 |
| --------- | ------ | ------------------------------------------- |
| `code`    | string | Machine-readable error code                 |
| `message` | string | Human-readable error message                |
| `field`   | string | Field that caused the error (if applicable) |
| `status`  | number | HTTP status code                            |
| `details` | object | Additional error details (optional)         |

## HTTP Status Codes

### 2xx Success

| Code  | Name       | Description                             |
| ----- | ---------- | --------------------------------------- |
| `200` | OK         | Request successful                      |
| `201` | Created    | Resource created successfully           |
| `204` | No Content | Request successful, no content returned |

### 4xx Client Errors

| Code  | Name                 | Description                          |
| ----- | -------------------- | ------------------------------------ |
| `400` | Bad Request          | Invalid request format or parameters |
| `401` | Unauthorized         | Missing or invalid API key           |
| `403` | Forbidden            | API key lacks required permissions   |
| `404` | Not Found            | Resource doesn't exist               |
| `409` | Conflict             | Request conflicts with current state |
| `422` | Unprocessable Entity | Validation failed                    |
| `429` | Too Many Requests    | Rate limit exceeded                  |

### 5xx Server Errors

| Code  | Name                  | Description                     |
| ----- | --------------------- | ------------------------------- |
| `500` | Internal Server Error | Something went wrong on our end |
| `502` | Bad Gateway           | Upstream service error          |
| `503` | Service Unavailable   | API temporarily offline         |
| `504` | Gateway Timeout       | Request took too long           |

## Error Codes

### Authentication Errors

| Code                       | HTTP Status | Description                                      |
| -------------------------- | ----------- | ------------------------------------------------ |
| `unauthorized`             | 401         | Missing or invalid API key                       |
| `invalid_api_key`          | 401         | API key format is incorrect                      |
| `expired_api_key`          | 401         | API key has been revoked                         |
| `insufficient_permissions` | 403         | API key lacks required permissions               |
| `wrong_environment`        | 401         | Using sandbox key on live endpoint or vice versa |

```json theme={null}
{
  "error": {
    "code": "unauthorized",
    "message": "Invalid API key provided",
    "status": 401
  }
}
```

### Validation Errors

| Code                     | HTTP Status | Description                |
| ------------------------ | ----------- | -------------------------- |
| `validation_error`       | 422         | Request validation failed  |
| `missing_required_field` | 422         | Required field is missing  |
| `invalid_format`         | 422         | Field format is incorrect  |
| `invalid_value`          | 422         | Field value is not allowed |

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "Invalid phone number format",
    "field": "phone",
    "status": 422,
    "details": {
      "expected_format": "+234XXXXXXXXXX"
    }
  }
}
```

### Resource Errors

| Code                      | HTTP Status | Description                       |
| ------------------------- | ----------- | --------------------------------- |
| `resource_not_found`      | 404         | Resource doesn't exist            |
| `resource_already_exists` | 409         | Resource already exists           |
| `resource_deleted`        | 410         | Resource has been deleted         |
| `resource_locked`         | 423         | Resource is locked for processing |

```json theme={null}
{
  "error": {
    "code": "resource_not_found",
    "message": "Customer with ID 'cus_123' not found",
    "status": 404
  }
}
```

### Transaction Errors

| Code                    | HTTP Status | Description                            |
| ----------------------- | ----------- | -------------------------------------- |
| `insufficient_balance`  | 422         | Wallet balance is too low              |
| `invalid_amount`        | 422         | Amount is invalid or out of range      |
| `duplicate_transaction` | 409         | Transaction with same reference exists |
| `transaction_failed`    | 422         | Transaction processing failed          |
| `account_inactive`      | 422         | Account is not active                  |
| `daily_limit_exceeded`  | 422         | Daily transaction limit exceeded       |
| `invalid_bank_code`     | 422         | Bank code is not supported             |

```json theme={null}
{
  "error": {
    "code": "insufficient_balance",
    "message": "Insufficient wallet balance",
    "status": 422,
    "details": {
      "required": 10000,
      "available": 5000,
      "currency": "NGN"
    }
  }
}
```

### Rate Limiting Errors

| Code                  | HTTP Status | Description       |
| --------------------- | ----------- | ----------------- |
| `rate_limit_exceeded` | 429         | Too many requests |

```json theme={null}
{
  "error": {
    "code": "rate_limit_exceeded",
    "message": "Rate limit exceeded. Try again in 60 seconds",
    "status": 429,
    "details": {
      "retry_after": 60,
      "limit": 1000,
      "window": "1 minute"
    }
  }
}
```

### Server Errors

| Code                  | HTTP Status | Description             |
| --------------------- | ----------- | ----------------------- |
| `internal_error`      | 500         | Unexpected server error |
| `service_unavailable` | 503         | API temporarily offline |
| `gateway_timeout`     | 504         | Request timeout         |

```json theme={null}
{
  "error": {
    "code": "internal_error",
    "message": "An unexpected error occurred. Please try again",
    "status": 500,
    "details": {
      "request_id": "req_abc123"
    }
  }
}
```

## Handling Errors

### JavaScript/Node.js

```javascript theme={null}
try {
  const transfer = await client.transfers.create({
    amountMinor: 10000,
    recipient_account: '0123456789',
    recipient_bank_code: '058'
  });
  
  console.log('Transfer successful:', transfer.id);
  
} catch (error) {
  if (error.response) {
    const { code, message, status } = error.response.error;
    
    switch (code) {
      case 'insufficient_balance':
        console.error('Not enough funds:', message);
        // Show user balance top-up option
        break;
        
      case 'rate_limit_exceeded':
        console.error('Rate limited:', message);
        // Implement exponential backoff
        break;
        
      case 'validation_error':
        console.error('Validation failed:', message);
        // Show form error to user
        break;
        
      default:
        console.error('Error:', message);
    }
  } else {
    console.error('Network error:', error.message);
  }
}
```

### Python

```python theme={null}
from zentra import Client, ZentraError

client = Client(api_key='your_key')

try:
    transfer = client.transfers.create(
        amount=10000,
        recipient_account='0123456789',
        recipient_bank_code='058'
    )
    
    print(f'Transfer successful: {transfer.id}')
    
except ZentraError as e:
    if e.code == 'insufficient_balance':
        print(f'Not enough funds: {e.message}')
        # Handle insufficient balance
        
    elif e.code == 'rate_limit_exceeded':
        print(f'Rate limited: {e.message}')
        # Implement retry logic
        
    elif e.code == 'validation_error':
        print(f'Validation error: {e.message}')
        # Show error to user
        
    else:
        print(f'Error: {e.message}')
```

### PHP

```php theme={null}
use Zentra\Client;
use Zentra\Exception\ZentraException;

$client = new Client(['api_key' => 'your_key']);

try {
    $transfer = $client->transfers->create([
        'amount' => 10000,
        'recipient_account' => '0123456789',
        'recipient_bank_code' => '058'
    ]);
    
    echo "Transfer successful: {$transfer->id}";
    
} catch (ZentraException $e) {
    switch ($e->getCode()) {
        case 'insufficient_balance':
            echo "Not enough funds: {$e->getMessage()}";
            break;
            
        case 'rate_limit_exceeded':
            echo "Rate limited: {$e->getMessage()}";
            break;
            
        case 'validation_error':
            echo "Validation error: {$e->getMessage()}";
            break;
            
        default:
            echo "Error: {$e->getMessage()}";
    }
}
```

## Best Practices

<AccordionGroup>
  <Accordion title="Always Handle Errors">
    Wrap all API calls in try-catch blocks and handle different error types appropriately.
  </Accordion>

  <Accordion title="Use Error Codes, Not Messages">
    Error messages may change. Always check the `code` field for programmatic error handling.
  </Accordion>

  <Accordion title="Log Request IDs">
    Include the `request_id` from error responses when contacting support.
  </Accordion>

  <Accordion title="Implement Retry Logic">
    For `5xx` errors and `rate_limit_exceeded`, implement exponential backoff retry logic.
  </Accordion>

  <Accordion title="Show User-Friendly Messages">
    Don't expose raw error messages to users. Map errors to friendly messages.
  </Accordion>

  <Accordion title="Monitor Error Rates">
    Track error rates in your application to catch issues early.
  </Accordion>
</AccordionGroup>

## Retry Logic

For transient errors (network issues, timeouts, server errors), implement retry logic:

```javascript theme={null}
async function retryRequest(fn, maxRetries = 3) {
  let lastError;
  
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await fn();
    } catch (error) {
      lastError = error;
      
      // Only retry on specific errors
      if (error.status >= 500 || error.code === 'rate_limit_exceeded') {
        // Exponential backoff: 1s, 2s, 4s
        const delay = Math.pow(2, i) * 1000;
        await new Promise(resolve => setTimeout(resolve, delay));
        continue;
      }
      
      // Don't retry client errors
      throw error;
    }
  }
  
  throw lastError;
}

// Usage
const transfer = await retryRequest(() => 
  client.transfers.create({
    amountMinor: 10000,
    destinationAccountNumber: '0123456789',
    destinationBankCode: '058'
  })
);
```

## Error Monitoring

Integrate error tracking tools like Sentry:

```javascript theme={null}
const Sentry = require('@sentry/node');

try {
  await client.transfers.create({...});
} catch (error) {
  // Log to Sentry
  Sentry.captureException(error, {
    tags: {
      service: 'zentra',
      error_code: error.code
    },
    extra: {
      request_id: error.request_id
    }
  });
  
  throw error;
}
```

## Getting Help

If you encounter errors you don't understand:

1. Check this error reference
2. Search our [FAQ](/support/faq)
3. Contact support with the `request_id` from the error response
4. Review [Support and escalation paths](/support/contact)

## Next Steps

<CardGroup cols={2}>
  <Card title="Rate Limits" icon="gauge" href="/api-reference/rate-limits">
    Learn about rate limiting
  </Card>

  <Card title="Webhooks" icon="webhook" href="/api-reference/webhooks/overview">
    Handle events asynchronously
  </Card>

  <Card title="Testing" icon="flask" href="/resources/sandbox">
    Test error scenarios in sandbox
  </Card>

  <Card title="FAQ" icon="question" href="/support/faq">
    Common questions and answers
  </Card>
</CardGroup>
