Custom error classes extend Error
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:
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 })
}
DetectionPro
The detection method for this pattern is part of AuditBuffet Pro.
The full method (what to look for, pass and fail conditions, and how results get reported) ships inside the Code Quality Essentials Audit. The pattern definition, severity rationale, and remediation remain openly licensed under CC-BY-4.0.
External references
- iso-25010:2011 · maintainability.modifiability — Maintainability — Modifiability
Taxons
History
- 2026-04-18·v1.0.0·Initial import from code-quality-essentials·automated