agentsclimarketplace

Express error handling

Skill VersoXBT/claude-initial-setup/skills/express-node/express-error-handling

Express.js error handling patterns including async error catching, custom error classes, centralized error middleware, and the distinction between operational and programmer errors. Use when the user is handling errors in Express, writing async route handlers, creating custom error types, or debugging unhandled rejections. Trigger on mentions of Express error handling, async errors, error middleware, AppError, or unhandled exceptions in Node.js.From its SKILL.md

Install
npx -y skills add VersoXBT/claude-initial-setup --skill express-error-handling

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

SKILL.md

6.4 KB, ~1.3k tokens by cl100k_base, as published. Nobody here has run it

Express Error Handling

Patterns for comprehensive, centralized error handling in Express.js applications.

When to Use

  • User is writing Express route handlers with async operations
  • User needs custom error classes for different HTTP status codes
  • User asks about centralized error handling
  • User has unhandled promise rejections or uncaught exceptions
  • User needs to distinguish between operational and programmer errors

Core Patterns

Custom Error Classes

Create a hierarchy of application errors that carry HTTP status codes and operational flags.

export class AppError extends Error {
  readonly statusCode: number
  readonly isOperational: boolean

  constructor(message: string, statusCode: number, isOperational = true) {
    super(message)
    this.statusCode = statusCode
    this.isOperational = isOperational
    Object.setPrototypeOf(this, new.target.prototype)
  }
}

export class NotFoundError extends AppError {
  constructor(resource: string) {
    super(`${resource} not found`, 404)
  }
}

export class ValidationError extends AppError {
  readonly details: Record<string, string[]>

  constructor(details: Record<string, string[]>) {
    super('Validation failed', 400)
    this.details = details
  }
}

export class UnauthorizedError extends AppError {
  constructor(message = 'Authentication required') {
    super(message, 401)
  }
}

export class ForbiddenError extends AppError {
  constructor(message = 'Insufficient permissions') {
    super(message, 403)
  }
}

export class ConflictError extends AppError {
  constructor(message: string) {
    super(message, 409)
  }
}

Async Error Wrapper

Express does not catch errors from async route handlers automatically. Wrap them to forward errors to the error middleware.

import { Request, Response, NextFunction, RequestHandler } from 'express'

function asyncHandler(
  fn: (req: Request, res: Response, next: NextFunction) => Promise<void>
): RequestHandler {
  return (req, res, next) => {
    fn(req, res, next).catch(next)
  }
}

// Usage -- errors are automatically forwarded to error middleware
router.get(
  '/users/:id',
  asyncHandler(async (req, res) => {
    const user = await db.user.findUnique({ where: { id: req.params.id } })
    if (!user) throw new NotFoundError('User')
    res.json({ data: user })
  })
)

Centralized Error Middleware

A single error handler that formats all errors consistently. Must have exactly 4 parameters.

import { Request, Response, NextFunction } from 'express'
import { AppError, ValidationError } from './errors'

interface ErrorResponse {
  error: string
  details?: Record<string, string[]>
  stack?: string
}

function errorHandler(err: Error, req: Request, res: Response, _next: NextFunction): void {
  if (err instanceof ValidationError) {
    const body: ErrorResponse = { error: err.message, details: err.details }
    res.status(err.statusCode).json(body)
    return
  }

  if (err instanceof AppError) {
    const body: ErrorResponse = { error: err.message }
    res.status(err.statusCode).json(body)
    return
  }

  // Programmer error -- do not leak internals
  console.error('Unexpected error:', err)
  const body: ErrorResponse = { error: 'Internal server error' }
  if (process.env.NODE_ENV === 'development') {
    body.stack = err.stack
  }
  res.status(500).json(body)
}

export { errorHandler }

Operational vs Programmer Errors

Operational errors are expected (invalid input, resource not found, network timeout). Programmer errors are bugs (TypeError, undefined access). Handle them differently.

// Operational -- expected, recoverable
throw new NotFoundError('User')
throw new ValidationError({ email: ['Invalid email format'] })

// Programmer -- unexpected, indicates a bug
// These should crash the process in production (after cleanup)
const user = undefined
user.name  // TypeError -- programmer error

// In production, catch unhandled errors and restart gracefully
process.on('uncaughtException', (err) => {
  console.error('UNCAUGHT EXCEPTION -- shutting down:', err)
  server.close(() => process.exit(1))
})

process.on('unhandledRejection', (reason) => {
  console.error('UNHANDLED REJECTION -- shutting down:', reason)
  server.close(() => process.exit(1))
})

404 Handler

Catch requests that do not match any route. Register after all routes but before the error handler.

function notFoundHandler(req: Request, res: Response, _next: NextFunction): void {
  res.status(404).json({
    error: `Cannot ${req.method} ${req.path}`,
  })
}

// Registration order
app.use('/api', apiRoutes)
app.use(notFoundHandler)  // After routes
app.use(errorHandler)     // After 404

Anti-Patterns

  • try/catch in every single route handler -- Use the asyncHandler wrapper instead. Centralize error formatting in the error middleware, not in each handler.
  • Sending error response AND calling next(err) -- This causes "headers already sent" errors. Do one or the other, never both.
  • Swallowing errors silently -- catch () {} hides bugs. Always log unexpected errors and forward them to the error handler.
  • Leaking stack traces in production -- Never send err.stack or internal details in production responses. Attackers use these to find vulnerabilities.
  • Treating all errors the same -- A validation error (400) and a database connection failure (500) need different handling. Use the AppError hierarchy to distinguish them.

Quick Reference

Error hierarchy:
  AppError (base)
    NotFoundError (404)
    ValidationError (400)
    UnauthorizedError (401)
    ForbiddenError (403)
    ConflictError (409)

Middleware registration order:
  1. Routes
  2. 404 handler (3 params)
  3. Error handler (4 params)

Async pattern:
  router.get('/path', asyncHandler(async (req, res) => { ... }))

Process-level:
  process.on('uncaughtException', ...)
  process.on('unhandledRejection', ...)

What ships with it

Read from the repository

Just SKILL.md. No reference files, no scripts.

Gives 0 of the 12 instructions most error diagnosis skills give in ~1.3k tokens

Counted across 135 of the 162 authors here whose files we hold, read 2026-09-06

  • Handle, re-throw, or log in every catch blockin 12 of 135, across 7 files
  • Use typed error classes over string messagesin 11 of 135, across 6 files
  • Log full error context server-sidein 10 of 135, across 5 files
  • Document every error code clients may receivein 9 of 135, across 4 files
  • Surface errors at the boundary where they occurin 9 of 135, across 4 files
  • Wrap React components in an ErrorBoundaryin 9 of 135, across 4 files
  • Wrap errors with context, never lose the originalin 9 of 135, across 4 files
  • Use the standard error envelope for API responsesin 9 of 135, across 4 files
  • Retry only retriable errors, never 4xx client errorsin 8 of 135, across 3 files
  • Retry transient failures with exponential backoff and jitterin 8 of 135
  • Show users friendly messages without technical detailsin 7 of 135, across 3 files
  • Use the Result pattern for expected failuresin 7 of 135, across 5 files

Said here and by no other author read

  • Wrap async route handlers to forward errors
  • Create custom error classes carrying HTTP status codes
  • Use a single centralized error middleware
  • Give the error handler exactly four parameters
  • Distinguish operational errors from programmer errors
  • Register the 404 handler after all routes

Grouped from the skills themselves: near-identical wordings counted once, and counted by distinct author, so one author publishing three of these counts once. Length counted with cl100k_base; the agent that loads this file may tokenize it differently.

Keep looking

Skills are one crate of 325,949. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.