Skip to main content
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 CodeError TypeDescription
200SuccessRequest completed successfully
400Bad RequestInvalid request format or parameters
401UnauthorizedInvalid or missing API key
403ForbiddenInsufficient permissions
429Too Many RequestsRate limit exceeded
500Internal Server ErrorPromptGuard system error
502Bad GatewayUpstream provider error
503Service UnavailableTemporary service outage

Error Response Format

All errors follow a consistent JSON structure:
{
  "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:
{
  "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"
    }
  }
}
Handling Strategy:
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:
{
  "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:
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:
{
  "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:
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:
{
  "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"
  }
}
Graceful Security Handling:
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:
{
  "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:
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

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

// 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

// 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

Need help implementing robust error handling? Contact support for guidance on production-ready error handling strategies.