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.
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:Copy
{
"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:Copy
{
"error": {
"message": "Invalid API key provided",
"type": "authentication_error",
"code": "invalid_api_key",
"details": {
"format_expected": "pg_live_xxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxx",
"suggestion": "Verify your API key in the dashboard"
}
}
}
Copy
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.startsWith('pg_')) {
throw new Error('Invalid API key format. Expected: pg_live_... or pg_test_...');
}
// 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:Copy
{
"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"
}
}
}
Copy
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:Copy
{
"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
}
}
}
Copy
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-4',
messages: [{ role: 'user', content: prompt }]
});
});
}
Security Policy Errors
Request Blocked (400)
Error Response:Copy
{
"error": {
"message": "Request blocked by security policy",
"type": "policy_violation",
"code": "prompt_injection_detected",
"details": {
"threat_type": "prompt_injection",
"confidence": 0.95,
"rule_triggered": "injection_v2_strict",
"suggestion": "Rephrase your request to avoid instruction overrides"
},
"event_id": "evt_abc123def456"
}
}
Copy
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 === 400 && errorData?.type === 'policy_violation') {
const threatType = errorData.details?.threat_type;
const confidence = errorData.details?.confidence;
const eventId = errorData.event_id;
// 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:Copy
{
"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"
}
}
}
Copy
class ProviderFailoverHandler:
def __init__(self):
self.provider_preference = ['openai', 'anthropic']
self.model_mapping = {
'gpt-4': 'claude-3-5-sonnet-latest',
'gpt-3.5-turbo': 'claude-3-haiku-20240307'
}
async def request_with_failover(self, prompt, model='gpt-4'):
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
Copy
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
Copy
// 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
Copy
// 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
Best Practices
Production deployment and reliability best practices
Rate Limits
Understanding and managing API rate limits
Troubleshooting
Common issues and debugging techniques
Monitoring
Set up comprehensive monitoring and alerts