Add Secure Error Handling with AI Prompts

Share

TL;DR

Error messages help attackers. Stack traces reveal file paths, database errors expose schema, and verbose errors guide exploitation. Show generic messages to users, log details server-side. These prompts help you implement secure error handling patterns.

Error Handler Middleware

Express Error Handler

Add secure error handling to my Express application.

// Custom error class class AppError extends Error { constructor(message, statusCode, isOperational = true) { super(message); this.statusCode = statusCode; this.isOperational = isOperational; // Expected errors vs bugs Error.captureStackTrace(this, this.constructor); } }

// Error handler middleware (must be last) const errorHandler = (err, req, res, next) => { // Log full error for debugging console.error('Error:', { message: err.message, stack: err.stack, url: req.url, method: req.method, userId: req.user?.id, timestamp: new Date().toISOString() });

// Determine status code const statusCode = err.statusCode || 500;

// Response based on environment if (process.env.NODE_ENV === 'production') { // Generic message for production res.status(statusCode).json({ error: statusCode === 500 ? 'An unexpected error occurred' : err.message }); } else { // Detailed for development res.status(statusCode).json({ error: err.message, stack: err.stack }); } };

app.use(errorHandler);

Usage: throw new AppError('Resource not found', 404); throw new AppError('Invalid input', 400);

API Error Responses

Secure API Error Responses

Make my API error responses secure and consistent.

// BAD - reveals too much { "error": "SequelizeDatabaseError: relation "users" does not exist", "query": "SELECT * FROM users WHERE id = 1", "stack": "at Query.run (/app/node_modules/sequelize/..." }

// GOOD - safe for production { "error": { "code": "NOT_FOUND", "message": "The requested resource was not found" } }

Error response patterns:

// 400 Bad Request { "error": { "code": "INVALID_INPUT", "message": "Email format is invalid" }}

// 401 Unauthorized { "error": { "code": "UNAUTHORIZED", "message": "Authentication required" }}

// 403 Forbidden (don't reveal resource exists) { "error": { "code": "NOT_FOUND", "message": "Resource not found" }}

// 404 Not Found { "error": { "code": "NOT_FOUND", "message": "Resource not found" }}

// 429 Rate Limited { "error": { "code": "RATE_LIMITED", "message": "Too many requests" }}

// 500 Internal Error (never reveal details) { "error": { "code": "INTERNAL_ERROR", "message": "An unexpected error occurred" }}

Include request ID for support: { "error": { "code": "INTERNAL_ERROR", "message": "...", "requestId": "abc123" }}

Forbidden vs Not Found: Don't tell attackers a resource exists but they can't access it. For sensitive resources, return 404 instead of 403 to avoid revealing existence.

Database Error Handling

Sanitize Database Errors

Handle database errors without exposing sensitive details.

Database errors often contain:

  • Table/column names
  • SQL queries
  • Connection strings
  • Schema information

Wrap database calls:

async function safeDbQuery(queryFn) { try { return await queryFn(); } catch (error) { // Log full error for debugging console.error('Database error:', { message: error.message, code: error.code, query: error.sql, // Log but don't expose stack: error.stack });

// Return generic error
if (error.code === '23505') { // Unique violation
  throw new AppError('This record already exists', 409);
}
if (error.code === '23503') { // Foreign key violation
  throw new AppError('Referenced record not found', 400);
}

// Generic for unknown database errors
throw new AppError('Database operation failed', 500);

} }

// Usage const user = await safeDbQuery(() => db.users.findUnique({ where: { id } }) );

Never pass database error messages directly to responses.

Frontend Error Handling

React Error Boundaries

Add error boundaries that don't leak information in React.

class ErrorBoundary extends React.Component { state = { hasError: false, errorId: null };

static getDerivedStateFromError(error) { return { hasError: true }; }

componentDidCatch(error, errorInfo) { // Generate unique error ID for support const errorId = crypto.randomUUID(); this.setState({ errorId });

// Log to error tracking (Sentry, etc.)
// Don't show these details to users
logError({
  errorId,
  error: error.message,
  stack: error.stack,
  componentStack: errorInfo.componentStack,
});

}

render() { if (this.state.hasError) { return (

      Something went wrong
      We've been notified and are working on it.
      {this.state.errorId && (
        Error ID: {this.state.errorId}
      )}
       window.location.reload()}>
        Try Again


  );
}

return this.props.children;

} }

// Wrap your app

Never display error.message or stack traces to users in production.

Pro tip: Use error tracking services like Sentry or LogRocket. They capture full error details for debugging while showing users friendly messages. Include request IDs to correlate user reports with logs.

How do I debug production errors without verbose messages?

Use error tracking services (Sentry, Bugsnag), structured logging with request IDs, and log aggregation. You get full details in your tools without exposing them to attackers.

Should I show different errors for development?

Yes, but be careful. Check NODE_ENV to show detailed errors only in development. Never deploy with development error handling active.

Find Verbose Errors

Scan your application for error messages that leak sensitive information.

Start Free Scan
AI Fix Prompts

Add Secure Error Handling with AI Prompts