Error Types & the Error Object
Before you can handle errors well you need to know what you're dealing with. JavaScript has a base Error class and several built-in subtypes; Node adds system errors with code properties; libraries throw their own shapes. Understanding what each error looks like — and what properties it carries — is what lets you catch the right things, classify them correctly, and respond appropriately.
The base Error object
const err = new Error('Something went wrong')
err.message // 'Something went wrong'
err.name // 'Error'
err.stack // multiline string: message + call frames
// "Error: Something went wrong\n at Object.<anonymous> ..."
// You can attach custom properties:
err.statusCode = 400
err.code = 'VALIDATION_ERROR'Built-in Error subtypes
Type | When thrown |
|---|---|
| Wrong type passed — |
| Value out of allowed range — |
| Undeclared variable accessed |
| Invalid JavaScript (usually in |
| Malformed URI in |
// Catch a specific subtype:
try {
JSON.parse('{bad json}')
} catch (err) {
if (err instanceof SyntaxError) {
console.log('Invalid JSON:', err.message)
} else {
throw err // re-throw what we didn't expect
}
}Node system errors — the `code` property
import { readFile } from 'node:fs/promises'
try {
await readFile('/no/such/file.txt')
} catch (err) {
err.code // 'ENOENT' — no such file or directory
err.syscall // 'open'
err.path // '/no/such/file.txt'
err instanceof Error // true
}Code | Meaning |
|---|---|
| No such file or directory |
| Permission denied |
| Address (port) already in use |
| Connection refused (DB/service down) |
| Connection timed out |
| Connection forcibly closed by the peer |
HTTP errors — statusCode by convention
// A convention many apps adopt — attach statusCode to the Error:
class HttpError extends Error {
constructor(statusCode, message, code) {
super(message)
this.name = 'HttpError'
this.statusCode = statusCode
this.code = code
}
}
throw new HttpError(404, 'User not found', 'USER_NOT_FOUND')
throw new HttpError(400, 'Email is required', 'VALIDATION_ERROR')
throw new HttpError(409, 'Email already in use', 'DUPLICATE_EMAIL')
// In the centralised error handler:
res.status(err.statusCode ?? 500).json({ error: err.message, code: err.code })Library and ORM errors
// Prisma unique constraint violation:
import { Prisma } from '@prisma/client'
catch (err) {
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2002') {
throw new HttpError(409, 'Email already in use', 'DUPLICATE_EMAIL')
}
throw err // unexpected — re-throw
}
// pg (Postgres driver) constraint violation:
catch (err) {
if (err.code === '23505') { // unique_violation
throw new HttpError(409, 'Duplicate value', 'DUPLICATE')
}
throw err
}