# Custom error classes extend Error

- **Pattern:** `ab-002161` (`code-quality-essentials.organization.custom-error-classes`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-002161
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-002161)

## Why it matters

Throwing strings, numbers, or plain objects instead of `Error` instances destroys the stack trace entirely — the thrown value arrives at the `catch` handler with no indication of where in the call stack the problem originated. ISO 25010:2011 §6.5.4 classifies this as a reliability defect. Beyond diagnostics, plain-object throws break `instanceof` checks that callers use to distinguish error types: a catch handler checking `if (err instanceof NotFoundError)` silently falls through when the throw was `{ code: 404, text: '...' }`.

## Severity rationale

Low because non-Error throws do not prevent the exception from being caught, but they destroy stack traces and break instanceof-based error discrimination, making errors significantly harder to diagnose and handle correctly.

## Remediation

Always throw `Error` instances. For typed error handling, define custom classes that extend `Error` and set the prototype correctly for correct `instanceof` behavior after TypeScript compilation:

```typescript
export class UserNotFoundError extends Error {
  constructor(public readonly userId: string) {
    super(`User not found: ${userId}`)
    this.name = 'UserNotFoundError'
    // Required for instanceof to work correctly when compiled to ES5:
    Object.setPrototypeOf(this, new.target.prototype)
  }
}

// Throw:
throw new UserNotFoundError(id)

// Catch with discrimination:
if (error instanceof UserNotFoundError) {
  return Response.json({ error: 'User not found' }, { status: 404 })
}
```

## Detection

- **ID:** `custom-error-classes`
- **Severity:** `low`
- **What to look for:** Search source files for `throw` statements. Check what is being thrown: `throw new Error(...)` and `throw new CustomError(...)` are correct. Look for anti-patterns: `throw 'some string'`, `throw { message: '...' }`, `throw { code: 404, text: 'Not found' }`. Plain objects and strings thrown as errors lose the stack trace entirely and are not catchable with `instanceof` checks. Also check custom error class definitions — they should extend `Error` and call `super(message)` in the constructor. In TypeScript, custom errors also need `Object.setPrototypeOf(this, new.target.prototype)` for correct `instanceof` behavior when transpiled.
- **Pass criteria:** Enumerate all relevant code locations. All `throw` statements throw `Error` instances or classes that extend `Error`. Custom error classes call `super(message)` and set the prototype correctly with at least 1 verified location.
- **Fail criteria:** Strings, plain objects, or numbers are thrown instead of Error instances.
- **Skip (N/A) when:** No custom error handling code exists in the project (no throw statements beyond those in third-party code).
- **Detail on fail:** `"Code throws raw strings or plain objects instead of Error instances; stack traces are lost and instanceof checks cannot be used"`
- **Remediation:** Always throw Error instances. For custom error types:

  ```typescript
  // Bad: throws string (no stack trace, no instanceof check)
  throw 'User not found'

  // Bad: throws plain object (no prototype chain, no stack trace)
  throw { code: 404, message: 'Not found' }

  // Good: custom error class with full stack trace and instanceof support
  export class UserNotFoundError extends Error {
    constructor(public readonly userId: string) {
      super(`User not found: ${userId}`)
      this.name = 'UserNotFoundError'
      // Required for correct instanceof behavior when compiled to ES5:
      Object.setPrototypeOf(this, new.target.prototype)
    }
  }

  // Usage:
  throw new UserNotFoundError(id)

  // Catch:
  if (error instanceof UserNotFoundError) {
    return Response.json({ error: 'User not found' }, { status: 404 })
  }
  ```

## External references

- iso-25010 maintainability.modifiability

Taxons: code-quality

HTML version: https://auditbuffet.com/patterns/ab-002161
