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

# Error Handling

> Comprehensive error handling strategies for PromptGuard integration

<Info>
  Robust error handling is essential for production AI applications. This guide covers all error scenarios you may encounter with PromptGuard and how to handle them gracefully.
</Info>

## Error Types Overview

### HTTP Status Codes

PromptGuard uses standard HTTP status codes with detailed error information:

| Status Code | Error Type            | Description                          |
| ----------- | --------------------- | ------------------------------------ |
| **200**     | Success               | Request completed successfully       |
| **400**     | Bad Request           | Invalid request format or parameters |
| **401**     | Unauthorized          | Invalid or missing API key           |
| **403**     | Forbidden             | Insufficient permissions             |
| **429**     | Too Many Requests     | Rate limit exceeded                  |
| **500**     | Internal Server Error | PromptGuard system error             |
| **502**     | Bad Gateway           | Upstream provider error              |
| **503**     | Service Unavailable   | Temporary service outage             |

### Error Response Format

All errors follow a consistent JSON structure:

```json theme={"system"}
{
  "error": {
    "message": "Human-readable error description",
    "type": "error_category",
    "code": "specific_error_code",
    "details": {
      "field": "Additional context",
      "suggestion": "How to fix the issue"
    },
    "request_id": "req_abc123def456",
    "timestamp": "2024-01-15T10:30:00Z"
  }
}
```

## Authentication Errors

### Invalid API Key (401)

**Error Response:**

```json theme={"system"}
{
  "error": {
    "message": "Invalid API key provided",
    "type": "authentication_error",
    "code": "invalid_api_key",
    "details": {
      "suggestion": "Verify your API key in the dashboard"
    }
  }
}
```

**Handling Strategy:**

```javascript theme={"system"}
async function handleAuthError(error) {
  if (error.status === 401) {
    // Log the authentication failure
    console.error('Authentication failed:', error.message);

    // Check API key configuration
    const apiKey = process.env.PROMPTGUARD_API_KEY;
    if (!apiKey) {
      throw new Error('PROMPTGUARD_API_KEY environment variable not set');
    }

    if (!apiKey) {
      throw new Error('API key not configured. Please set PROMPTGUARD_API_KEY environment variable.');
    }

    // For production apps, notify operations team
    await notifyOpsTeam('PromptGuard authentication failure', {
      apiKey: apiKey.substring(0, 10) + '...',
      timestamp: new Date().toISOString()
    });

    throw new Error('Authentication failed. Please check your API key.');
  }
}
```

### Insufficient Permissions (403)

**Error Response:**

```json theme={"system"}
{
  "error": {
    "message": "API key lacks required permissions",
    "type": "permission_error",
    "code": "insufficient_permissions",
    "details": {
      "required_permission": "write",
      "current_permissions": ["read"],
      "suggestion": "Update API key permissions in dashboard"
    }
  }
}
```

**Handling Strategy:**

```python theme={"system"}
def handle_permission_error(error):
    if error.status_code == 403:
        error_data = error.response.json()

        required_perm = error_data['error']['details']['required_permission']
        current_perms = error_data['error']['details']['current_permissions']

        logging.warning(
            f"Insufficient permissions. Required: {required_perm}, "
            f"Current: {current_perms}"
        )

        # Provide clear feedback to developers
        raise PermissionError(
            f"API key needs '{required_perm}' permission. "
            f"Current permissions: {current_perms}. "
            f"Update permissions in PromptGuard dashboard."
        )
```

## Rate Limiting Errors

### Rate Limit Exceeded (429)

**Error Response:**

```json theme={"system"}
{
  "error": {
    "message": "Rate limit exceeded",
    "type": "rate_limit_error",
    "code": "requests_per_minute_exceeded",
    "details": {
      "limit": 1000,
      "used": 1000,
      "reset_time": "2024-01-15T10:31:00Z",
      "retry_after": 45
    }
  }
}
```

**Advanced Retry Logic:**

```javascript theme={"system"}
class RateLimitHandler {
  constructor(maxRetries = 3, baseDelay = 1000) {
    this.maxRetries = maxRetries;
    this.baseDelay = baseDelay;
  }

  async executeWithRetry(requestFn) {
    for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
      try {
        return await requestFn();

      } catch (error) {
        if (error.status === 429 && attempt < this.maxRetries) {
          const retryDelay = this.calculateRetryDelay(error, attempt);

          console.log(
            `Rate limited. Attempt ${attempt + 1}/${this.maxRetries + 1}. ` +
            `Retrying in ${retryDelay}ms`
          );

          await this.sleep(retryDelay);
          continue;
        }

        throw error;
      }
    }
  }

  calculateRetryDelay(error, attempt) {
    // Use server-provided retry-after if available
    const retryAfter = error.details?.retry_after;
    if (retryAfter) {
      return retryAfter * 1000; // Convert to milliseconds
    }

    // Use reset time if available
    const resetTime = error.details?.reset_time;
    if (resetTime) {
      const resetMs = new Date(resetTime).getTime();
      const nowMs = Date.now();
      const waitTime = Math.max(1000, resetMs - nowMs);
      return Math.min(waitTime, 60000); // Cap at 1 minute
    }

    // Fallback to exponential backoff
    const exponentialDelay = this.baseDelay * Math.pow(2, attempt);
    const jitter = Math.random() * 1000;
    return exponentialDelay + jitter;
  }

  sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }
}

// Usage
const rateLimitHandler = new RateLimitHandler();

async function makeResilientRequest(prompt) {
  return rateLimitHandler.executeWithRetry(async () => {
    return await openai.chat.completions.create({
      model: "gpt-5-nano",
      messages: [{ role: 'user', content: prompt }]
    });
  });
}
```

## Security Policy Errors

### Request Blocked (403)

When a request is blocked by a security policy, PromptGuard returns **HTTP 403** with a JSON body that includes `event_id` and optionally **`dashboard_url`** (a direct link to the event in the dashboard for audit and debugging).

**Error Response:**

```json theme={"system"}
{
  "error": {
    "message": "Request blocked by security policy",
    "type": "policy_violation",
    "code": "request_blocked",
    "event_id": "ac0ed289-1921-4d56-880b-063585836aea",
    "dashboard_url": "https://app.promptguard.co/dashboard/projects/{project_id}/interactions?event_id=ac0ed289-1921-4d56-880b-063585836aea",
    "details": {
      "threat_type": "prompt_injection",
      "confidence": 0.95
    }
  }
}
```

Use `event_id` (or open `dashboard_url` if present) to find the event in the dashboard under **Projects → Interactions** for full audit context.

**Graceful Security Handling:**

```javascript theme={"system"}
class SecurityErrorHandler {
  constructor(options = {}) {
    this.enableFallbacks = options.enableFallbacks || false;
    this.userFriendlyMessages = options.userFriendlyMessages || true;
  }

  async handleSecurityBlock(error, originalRequest) {
    const errorData = error.response?.data?.error;

    if (error.status === 403 && errorData?.type === 'policy_violation') {
      const threatType = errorData.details?.threat_type;
      const confidence = errorData.details?.confidence;
      const eventId = errorData.event_id;
      const dashboardUrl = errorData.dashboard_url; // optional deep link to event in dashboard

      // Log security event for analysis
      await this.logSecurityEvent({
        threatType,
        confidence,
        eventId,
        originalPrompt: this.sanitizeForLogging(originalRequest),
        timestamp: new Date().toISOString()
      });

      // Provide user-friendly response
      if (this.userFriendlyMessages) {
        return this.generateUserFriendlyResponse(threatType);
      }

      // Attempt content sanitization if enabled
      if (this.enableFallbacks) {
        return await this.attemptContentSanitization(originalRequest);
      }

      throw new SecurityBlockError({
        message: 'Request blocked by security policy',
        threatType,
        confidence,
        eventId,
        userMessage: this.generateUserFriendlyResponse(threatType)
      });
    }

    throw error;
  }

  generateUserFriendlyResponse(threatType) {
    const responses = {
      'prompt_injection':
        "I can't process requests that try to override my instructions. " +
        "Please rephrase your question in a straightforward way.",

      'data_exfiltration':
        "I can't provide information about my internal configuration. " +
        "How can I help you with your actual question?",

      'jailbreak_attempt':
        "I need to follow my safety guidelines. " +
        "Please ask me something I can help with appropriately.",

      'pii_detected':
        "I notice your message contains sensitive information. " +
        "Please remove any personal details and try again.",

      'default':
        "I can't process that request due to safety policies. " +
        "Please try rephrasing your question."
    };

    return responses[threatType] || responses.default;
  }

  async attemptContentSanitization(request) {
    // Attempt to clean the request and retry
    const sanitizedPrompt = this.sanitizePrompt(request.prompt);

    if (sanitizedPrompt !== request.prompt) {
      console.log('Attempting request with sanitized content');

      try {
        return await this.makeCleanRequest({
          ...request,
          prompt: sanitizedPrompt
        });
      } catch (retryError) {
        console.log('Sanitized request also blocked');
        throw retryError;
      }
    }

    throw new Error('Unable to sanitize content');
  }

  sanitizePrompt(prompt) {
    // Remove common injection patterns
    const patterns = [
      /ignore\s+(all\s+)?(previous|above|prior)\s+(instructions?|prompts?)/gi,
      /forget\s+(everything|all)\s+/gi,
      /you\s+are\s+now\s+/gi,
      /pretend\s+to\s+be\s+/gi
    ];

    let sanitized = prompt;
    patterns.forEach(pattern => {
      sanitized = sanitized.replace(pattern, '');
    });

    return sanitized.trim();
  }

  sanitizeForLogging(request) {
    // Remove sensitive data before logging
    return {
      ...request,
      prompt: request.prompt?.substring(0, 100) + '...',
      // Remove any API keys or tokens
      headers: undefined
    };
  }

  async logSecurityEvent(event) {
    // Send to your logging system
    console.log('Security Event:', JSON.stringify(event, null, 2));

    // Optionally send to external security monitoring
    if (process.env.SECURITY_WEBHOOK_URL) {
      try {
        await fetch(process.env.SECURITY_WEBHOOK_URL, {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify(event)
        });
      } catch (webhookError) {
        console.error('Failed to send security webhook:', webhookError);
      }
    }
  }
}
```

## Provider Errors

### Upstream Provider Issues (502)

**Error Response:**

```json theme={"system"}
{
  "error": {
    "message": "Upstream provider error",
    "type": "provider_error",
    "code": "openai_service_unavailable",
    "details": {
      "provider": "openai",
      "provider_error": "Service temporarily unavailable",
      "suggestion": "Retry with exponential backoff"
    }
  }
}
```

**Provider Failover Strategy:**

```python theme={"system"}
class ProviderFailoverHandler:
    def __init__(self):
        self.provider_preference = ['openai', 'anthropic']
        self.model_mapping = {
            'gpt-4': 'claude-haiku-4-5',
            'gpt-3.5-turbo': 'claude-haiku-4-5'
        }

    async def request_with_failover(self, prompt, model="gpt-5-nano"):
        primary_error = None

        try:
            # Try primary request
            return await self.make_request(prompt, model)

        except ProviderError as e:
            primary_error = e
            logging.warning(f"Primary provider failed: {e}")

            # Attempt failover if provider error
            if e.status_code in [502, 503, 504]:
                return await self.attempt_failover(prompt, model, primary_error)

            raise e

    async def attempt_failover(self, prompt, model, original_error):
        fallback_model = self.model_mapping.get(model)

        if not fallback_model:
            raise original_error

        try:
            logging.info(f"Attempting failover: {model} -> {fallback_model}")

            response = await self.make_request(prompt, fallback_model)

            # Log successful failover
            await self.log_failover_success(model, fallback_model)

            return response

        except Exception as failover_error:
            logging.error(f"Failover also failed: {failover_error}")

            # Return original error, not failover error
            raise original_error

    async def log_failover_success(self, original_model, fallback_model):
        logging.info(
            f"Successful failover from {original_model} to {fallback_model}"
        )

        # Track failover metrics
        await self.track_metric('provider_failover', {
            'original_model': original_model,
            'fallback_model': fallback_model,
            'timestamp': datetime.utcnow().isoformat()
        })
```

## Application-Level Error Handling

### Comprehensive Error Handler

```javascript theme={"system"}
class PromptGuardErrorHandler {
  constructor(options = {}) {
    this.enableRetries = options.enableRetries !== false;
    this.enableFallbacks = options.enableFallbacks || false;
    this.enableUserFriendlyMessages = options.enableUserFriendlyMessages !== false;
    this.logger = options.logger || console;
  }

  async handleRequest(requestFn, context = {}) {
    try {
      return await this.executeWithErrorHandling(requestFn, context);

    } catch (error) {
      return this.handleFinalError(error, context);
    }
  }

  async executeWithErrorHandling(requestFn, context) {
    const handlers = [
      this.handleRateLimit.bind(this),
      this.handleAuthentication.bind(this),
      this.handleSecurity.bind(this),
      this.handleProvider.bind(this),
      this.handleNetwork.bind(this)
    ];

    for (const handler of handlers) {
      try {
        return await handler(requestFn, context);
      } catch (error) {
        if (!error.canRetry) {
          throw error;
        }
        // Continue to next handler
      }
    }

    // If all handlers fail, execute the original request
    return await requestFn();
  }

  async handleRateLimit(requestFn, context) {
    const rateLimitHandler = new RateLimitHandler();
    return await rateLimitHandler.executeWithRetry(requestFn);
  }

  async handleAuthentication(requestFn, context) {
    try {
      return await requestFn();
    } catch (error) {
      if (error.status === 401) {
        await this.refreshApiKey(context);
        return await requestFn(); // Retry with new key
      }
      throw error;
    }
  }

  async handleSecurity(requestFn, context) {
    const securityHandler = new SecurityErrorHandler({
      enableFallbacks: this.enableFallbacks,
      userFriendlyMessages: this.enableUserFriendlyMessages
    });

    try {
      return await requestFn();
    } catch (error) {
      return await securityHandler.handleSecurityBlock(error, context);
    }
  }

  async handleProvider(requestFn, context) {
    const providerHandler = new ProviderFailoverHandler();
    return await providerHandler.request_with_failover(
      context.prompt,
      context.model
    );
  }

  async handleNetwork(requestFn, context) {
    // Handle network timeouts and connection errors
    const maxRetries = 3;

    for (let attempt = 0; attempt < maxRetries; attempt++) {
      try {
        return await requestFn();
      } catch (error) {
        if (this.isNetworkError(error) && attempt < maxRetries - 1) {
          const delay = 1000 * Math.pow(2, attempt);
          await this.sleep(delay);
          continue;
        }
        throw error;
      }
    }
  }

  isNetworkError(error) {
    return error.code === 'ECONNRESET' ||
           error.code === 'ETIMEDOUT' ||
           error.code === 'ENOTFOUND' ||
           error.message.includes('network') ||
           error.message.includes('timeout');
  }

  handleFinalError(error, context) {
    // Log the error with context
    this.logger.error('PromptGuard request failed', {
      error: error.message,
      status: error.status,
      context,
      timestamp: new Date().toISOString()
    });

    // Return user-friendly error
    if (this.enableUserFriendlyMessages) {
      return {
        success: false,
        error: 'ai_service_unavailable',
        message: 'AI service is temporarily unavailable. Please try again later.',
        details: {
          canRetry: true,
          estimatedRetryTime: this.estimateRetryTime(error)
        }
      };
    }

    throw error;
  }

  estimateRetryTime(error) {
    if (error.status === 429) {
      return error.details?.retry_after || 60;
    }
    if (error.status >= 500) {
      return 300; // 5 minutes for server errors
    }
    return 60; // 1 minute default
  }
}
```

## Error Monitoring and Alerting

### Error Tracking Integration

```javascript theme={"system"}
// Integration with error tracking services
class ErrorTracker {
  constructor(options = {}) {
    this.sentryDsn = options.sentryDsn;
    this.enableMetrics = options.enableMetrics !== false;
  }

  trackError(error, context = {}) {
    // Send to Sentry or similar service
    if (this.sentryDsn && typeof Sentry !== 'undefined') {
      Sentry.captureException(error, {
        tags: {
          service: 'promptguard',
          error_type: error.type,
          status_code: error.status
        },
        extra: context
      });
    }

    // Track custom metrics
    if (this.enableMetrics) {
      this.trackMetrics(error, context);
    }
  }

  trackMetrics(error, context) {
    // Send metrics to your monitoring system
    const metrics = {
      'promptguard.error.count': 1,
      'promptguard.error.by_status': { [error.status]: 1 },
      'promptguard.error.by_type': { [error.type]: 1 }
    };

    // Send to monitoring system (DataDog, New Relic, etc.)
    this.sendMetrics(metrics);
  }
}
```

## Testing Error Handling

### Error Simulation for Testing

```javascript theme={"system"}
// Mock error responses for testing
class PromptGuardErrorSimulator {
  static createRateLimitError() {
    return {
      status: 429,
      response: {
        data: {
          error: {
            message: "Rate limit exceeded",
            type: "rate_limit_error",
            code: "requests_per_minute_exceeded",
            details: {
              limit: 1000,
              used: 1000,
              reset_time: new Date(Date.now() + 60000).toISOString(),
              retry_after: 60
            }
          }
        }
      }
    };
  }

  static createSecurityError() {
    return {
      status: 400,
      response: {
        data: {
          error: {
            message: "Request blocked by security policy",
            type: "policy_violation",
            code: "prompt_injection_detected",
            details: {
              threat_type: "prompt_injection",
              confidence: 0.95,
              suggestion: "Rephrase your request"
            },
            event_id: "evt_test123"
          }
        }
      }
    };
  }
}

// Test error handling
describe('PromptGuard Error Handling', () => {
  test('handles rate limit errors with retry', async () => {
    const mockRequest = jest.fn()
      .mockRejectedValueOnce(PromptGuardErrorSimulator.createRateLimitError())
      .mockResolvedValueOnce({ choices: [{ message: { content: 'Success' } }] });

    const handler = new PromptGuardErrorHandler();
    const result = await handler.handleRequest(mockRequest);

    expect(mockRequest).toHaveBeenCalledTimes(2);
    expect(result.choices[0].message.content).toBe('Success');
  });

  test('handles security blocks gracefully', async () => {
    const mockRequest = jest.fn()
      .mockRejectedValue(PromptGuardErrorSimulator.createSecurityError());

    const handler = new PromptGuardErrorHandler({
      enableUserFriendlyMessages: true
    });

    const result = await handler.handleRequest(mockRequest);

    expect(result.success).toBe(false);
    expect(result.message).toContain('rephrase');
  });
});
```

## Next Steps

<CardGroup cols={2}>
  <Card title="Best Practices" icon="star" href="/production/best-practices">
    Production deployment and reliability best practices
  </Card>

  <Card title="Rate Limits" icon="gauge" href="/production/rate-limits">
    Understanding and managing API rate limits
  </Card>

  <Card title="Troubleshooting" icon="wrench" href="/production/troubleshooting">
    Common issues and debugging techniques
  </Card>

  <Card title="Monitoring" icon="chart-line" href="/platform/dashboard">
    Set up comprehensive monitoring and alerts
  </Card>
</CardGroup>

Need help implementing robust error handling? [Contact support](mailto:support@promptguard.co) for guidance on production-ready error handling strategies.
