# PERS Error Handling

## Error Response Structure

PERS API uses structured error responses for consistent error handling across all endpoints. All errors follow the same format with security-filtered messages and correlation IDs for support.

### Why Structured Errors?

- **Consistency:** All errors follow a uniform structure and categorization
- **Security:** Sensitive system details are filtered out; only safe messages are returned
- **Traceability:** Every error includes a correlation ID for support and debugging
- **Programmatic Handling:** Structured format enables reliable error processing


## API Error Format

All PERS API errors return structured JSON responses following RFC 7807 (Problem Details for HTTP APIs):

```json
{
  "status": 404,
  "title": "Resource Not Found",
  "detail": "User with ID 12345 could not be found",
  "message": "The requested user could not be found",
  "code": "USER_NOT_FOUND",
  "category": "DOMAIN_RULE",
  "timestamp": "2026-01-15T10:30:00.000Z",
  "correlationId": "pers-abc123-def456",
  "retryable": false,
  "domain": "user"
}
```

### Error Response Fields

| Field | Type | Description |
|  --- | --- | --- |
| `status` | number | HTTP status code |
| `title` | string | Human-readable error summary |
| `detail` | string | Specific error explanation |
| `message` | string | Error message (usually same as detail) |
| `code` | string | Error code for programmatic handling |
| `category` | string | Error classification (VALIDATION, SECURITY, etc.) |
| `timestamp` | string | ISO timestamp when error occurred |
| `correlationId` | string | Request correlation ID for distributed tracing and support |
| `retryable` | boolean | Whether operation can be retried |
| `domain` | string | Domain that generated the error (user, campaign, transaction, etc.) |
| `details` | object | Additional structured error context (optional) |
| `target` | string | Target property/parameter for validation errors (optional) |


### TypeScript Support

For TypeScript applications, import error types from the shared library:

```typescript
import type { StructuredError, ErrorCategory } from '@explorins/pers-shared';
```

## Error Categories & HTTP Status Mapping

| Category | HTTP Status | Description | Retryable |
|  --- | --- | --- | --- |
| VALIDATION | 400 | Invalid request data or format | No |
| SECURITY | 401/403 | Authentication/authorization failures | No |
| DOMAIN_RULE | 404/409/422 | Business logic and resource errors | No/Maybe |
| TECHNICAL | 500 | Application/configuration issues | Maybe |
| RATE_LIMIT | 429 | API rate limit exceeded | Yes |
| TIMEOUT | 504 | Request timeout | Yes |
| INFRASTRUCTURE | 503 | External service failures | Yes |
| UNKNOWN | 500 | Unclassified errors | Maybe |


## Common Error Codes by Domain

### User Domain

| Code | Description |
|  --- | --- |
| `USER_NOT_FOUND` | User with specified ID does not exist |
| `USER_ALREADY_EXISTS` | User with this identifier already exists |
| `USER_NOT_AUTHORIZED` | User lacks required permissions |


### Campaign Domain

| Code | Description |
|  --- | --- |
| `CAMPAIGN_NOT_FOUND` | Campaign does not exist |
| `CAMPAIGN_NOT_ACTIVE` | Campaign is not currently active |
| `CAMPAIGN_ALREADY_CLAIMED` | User has already claimed this campaign |
| `CAMPAIGN_CLAIM_LIMIT_REACHED` | User has reached claim limit |
| `CAMPAIGN_COOLDOWN_ACTIVE` | Claim cooldown period is active |
| `CAMPAIGN_CONDITION_NOT_MET` | Campaign conditions not satisfied |


### Transaction Domain

| Code | Description |
|  --- | --- |
| `TRANSACTION_NOT_FOUND` | Transaction does not exist |
| `TRANSACTION_ALREADY_COMPLETED` | Transaction was already processed |
| `TRANSACTION_EXPIRED` | Transaction has expired |
| `INSUFFICIENT_BALANCE` | Insufficient token balance |


### Authentication Domain

| Code | Description |
|  --- | --- |
| `AUTHENTICATION_REQUIRED` | Valid credentials required |
| `AUTHORIZATION_FAILED` | Insufficient permissions |
| `INVALID_TOKEN` | JWT token is invalid |
| `TOKEN_EXPIRED` | JWT token has expired |


### Webhook Domain

| Code | Description |
|  --- | --- |
| `WEBHOOK_NOT_FOUND` | Webhook configuration not found |
| `WEBHOOK_INACTIVE` | Webhook is disabled |
| `WEBHOOK_SOURCE_NOT_ALLOWED` | Caller source not in allowed list |
| `WEBHOOK_SIGNATURE_INVALID` | Signature verification failed |


### Generic Codes

| Code | Description |
|  --- | --- |
| `RESOURCE_NOT_FOUND` | Generic resource not found |
| `RESOURCE_CONFLICT` | Concurrent modification detected |
| `VALIDATION_ERROR` | Input validation failed |
| `BUSINESS_RULE_VIOLATION` | Business rule constraint violated |
| `INTERNAL_ERROR` | Unexpected server error |


## Error Handling Examples

### Example Error Response

**Authentication Error (401)**

```json
{
  "status": 401,
  "title": "Authentication Required",
  "detail": "Valid authentication credentials are required",
  "message": "Please log in to access this resource",
  "code": "AUTHENTICATION_REQUIRED",
  "category": "SECURITY",
  "timestamp": "2026-01-15T10:30:00.000Z",
  "correlationId": "pers-def456-ghi789",
  "retryable": false
}
```

**Validation Error (400)**

```json
{
  "status": 400,
  "title": "Validation Error",
  "detail": "The email field is required",
  "message": "The email field is required",
  "code": "VALIDATION_ERROR",
  "category": "VALIDATION",
  "timestamp": "2026-01-15T10:30:00.000Z",
  "correlationId": "pers-jkl012-mno345",
  "details": {
    "field": "email",
    "rejectedValue": null
  },
  "retryable": false
}
```

### Client-Side Error Handling

```javascript
// Example: Handling API errors in JavaScript/TypeScript
async function callAPI() {
  try {
    const response = await fetch('/api/users', {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer your-token',
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ name: 'John' })
    });
    
    if (!response.ok) {
      const error = await response.json();
      
      // Handle different error categories
      if (error.category === 'SECURITY') {
        // Redirect to login
        window.location.href = '/login';
      } else if (error.category === 'VALIDATION') {
        // Show validation errors to user
        showValidationError(error.details);
      } else {
        // Show generic error message
        showErrorMessage(error.message);
      }
      return;
    }
    
    const data = await response.json();
    // Handle success response
  } catch (networkError) {
    // Handle network errors
    showErrorMessage('Network error. Please try again.');
  }
}
```

## Advanced Error Features

### Correlation ID Tracking

Every request and error response includes a unique correlation ID for distributed tracing:

- Automatically generated for each request
- Included in response headers: `X-Correlation-ID`
- Persisted across service boundaries for end-to-end tracing
- Essential for debugging issues across distributed systems


### Security Filtering

The API implements intelligent message safety filtering:

- System internals (stack traces, database errors) are never exposed
- Sensitive data (secrets, keys, credentials) is automatically filtered
- Business-friendly error messages are preserved
- Technical errors are sanitized for user consumption


### Category-Specific Error Interfaces

For enhanced type safety in TypeScript applications:

```typescript
import type { 
  ValidationStructuredError,
  DomainRuleStructuredError,
  SecurityStructuredError,
  RateLimitStructuredError
} from '@explorins/pers-shared';

// Validation errors include field violations
interface ValidationStructuredError {
  category: 'VALIDATION';
  details: {
    violations: Array<{
      field: string;
      code: string;
      message: string;
      rejectedValue?: any;
    }>;
  };
  target: string; // Required field
  retryable: false;
}

// Rate limit errors include quota information
interface RateLimitStructuredError {
  category: 'RATE_LIMIT';
  details: {
    limit: number;
    remaining: number;
    resetTime: string; // ISO 8601
    window: string; // e.g., "1h", "1d"
  };
  retryable: true;
}
```

### Response Headers

Error responses include correlation tracking headers:

- `X-Correlation-ID`: Unique request identifier (set for all responses)
- `X-Response-Time`: ISO timestamp of response generation
- `Access-Control-Expose-Headers`: Exposes correlation ID for CORS requests


**Note**: Additional error metadata (`category`, `retryable`, etc.) is available in the response body, not as separate headers.

## Security Features

- System error details are never exposed to API consumers
- Sensitive data automatically filtered using pattern matching
- Every error includes a correlation ID for traceability
- All errors are logged with full context for debugging
- Consistent error categorization across all domains
- Message safety filtering prevents information leakage


For further details, see the [Authentication Guide](/4.authentication-guide) and [Developer Resources](/5.developer-resources).