Docs
4.7-Error-Types-Custom-Errors
4.7 Error Types and Custom Errors
Overview
JavaScript provides several built-in error types and allows you to create custom errors for better error handling and debugging.
Built-in Error Types
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
ā Error Hierarchy ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā¤
ā ā
ā Error ā
ā ā ā
ā āāāāāāāāāāā¬āāāāāāāāāā¬āā“āāāāāāāāā¬āāāāāāāāāā¬āāāāāāāāāā ā
ā ā ā ā ā ā ā ā
ā SyntaxError ReferenceError TypeError RangeError URIError ā
ā ā ā
ā EvalError ā
ā AggregateError ā
ā ā
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
Error Types Summary
| Error Type | When It Occurs |
|---|---|
Error | Generic error, base class |
SyntaxError | Invalid JavaScript syntax |
ReferenceError | Reference to undefined variable |
TypeError | Wrong type or null/undefined access |
RangeError | Number out of valid range |
URIError | Invalid URI encoding/decoding |
EvalError | Error with eval() (legacy) |
AggregateError | Multiple errors wrapped |
Error Object Properties
try {
throw new Error('Something went wrong');
} catch (error) {
console.log(error.name); // 'Error'
console.log(error.message); // 'Something went wrong'
console.log(error.stack); // Stack trace
// Some browsers also have:
console.log(error.fileName);
console.log(error.lineNumber);
console.log(error.columnNumber);
}
Built-in Error Examples
SyntaxError
// Caught at parse time (before execution)
// eval('const x = ;'); // SyntaxError: Unexpected token ';'
JSON.parse('{invalid}'); // SyntaxError: Unexpected token i
ReferenceError
console.log(undeclaredVariable); // ReferenceError
const obj = {};
obj.method(); // TypeError (not ReferenceError!)
TypeError
null.property; // TypeError: Cannot read properties of null
undefined.method(); // TypeError: Cannot read properties of undefined
const x = 5;
x(); // TypeError: x is not a function
const arr = 1;
arr[0]; // TypeError (in strict mode for assignment)
Object.freeze({}).x = 1; // TypeError in strict mode
RangeError
new Array(-1); // RangeError: Invalid array length
(1234).toString(100); // RangeError: radix must be 2-36
'x'.repeat(-1); // RangeError
function infinite() {
infinite();
} // RangeError: Maximum call stack exceeded
URIError
decodeURIComponent('%'); // URIError: URI malformed
AggregateError (ES2021)
try {
throw new AggregateError(
[new Error('Error 1'), new Error('Error 2'), new TypeError('Type Error')],
'Multiple errors occurred'
);
} catch (e) {
console.log(e.message); // 'Multiple errors occurred'
console.log(e.errors); // Array of errors
}
// Used with Promise.any()
Promise.any([Promise.reject('fail 1'), Promise.reject('fail 2')]).catch((e) => {
console.log(e instanceof AggregateError); // true
});
Creating Custom Errors
Basic Custom Error
class ValidationError extends Error {
constructor(message) {
super(message);
this.name = 'ValidationError';
}
}
throw new ValidationError('Invalid email format');
Custom Error with Additional Properties
class HttpError extends Error {
constructor(statusCode, message, body = null) {
super(message);
this.name = 'HttpError';
this.statusCode = statusCode;
this.body = body;
// Maintains proper stack trace (V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, HttpError);
}
}
get isClientError() {
return this.statusCode >= 400 && this.statusCode < 500;
}
get isServerError() {
return this.statusCode >= 500;
}
}
// Usage
try {
throw new HttpError(404, 'Resource not found', { id: 123 });
} catch (error) {
if (error instanceof HttpError) {
console.log(error.statusCode); // 404
console.log(error.isClientError); // true
}
}
Error Hierarchy
// Base application error
class AppError extends Error {
constructor(message, code) {
super(message);
this.name = this.constructor.name;
this.code = code;
this.timestamp = new Date();
}
toJSON() {
return {
name: this.name,
message: this.message,
code: this.code,
timestamp: this.timestamp,
};
}
}
// Specific error types
class ValidationError extends AppError {
constructor(field, message) {
super(message, 'VALIDATION_ERROR');
this.field = field;
}
}
class AuthenticationError extends AppError {
constructor(message = 'Authentication required') {
super(message, 'AUTH_ERROR');
}
}
class NotFoundError extends AppError {
constructor(resource, id) {
super(`${resource} with id ${id} not found`, 'NOT_FOUND');
this.resource = resource;
this.resourceId = id;
}
}
// Usage
try {
throw new NotFoundError('User', 123);
} catch (error) {
if (error instanceof NotFoundError) {
console.log(error.resource); // 'User'
console.log(error.resourceId); // 123
console.log(error.code); // 'NOT_FOUND'
}
}
Error Handling Patterns
Checking Error Type
try {
// Some operation
} catch (error) {
// By instanceof
if (error instanceof TypeError) {
console.log('Type error:', error.message);
} else if (error instanceof ReferenceError) {
console.log('Reference error:', error.message);
} else {
console.log('Unknown error:', error);
}
// By name
switch (error.name) {
case 'TypeError':
// handle
break;
case 'ValidationError':
// handle
break;
default:
throw error; // Re-throw if can't handle
}
}
Error Wrapping
class DatabaseError extends Error {
constructor(message, originalError) {
super(message);
this.name = 'DatabaseError';
this.originalError = originalError;
this.stack = `${this.stack}\nCaused by: ${originalError.stack}`;
}
}
async function queryDatabase(sql) {
try {
return await db.query(sql);
} catch (error) {
throw new DatabaseError(`Failed to execute query: ${sql}`, error);
}
}
Error Cause (ES2022)
try {
try {
throw new Error('Database connection failed');
} catch (dbError) {
throw new Error('Could not load user', { cause: dbError });
}
} catch (error) {
console.log(error.message); // 'Could not load user'
console.log(error.cause.message); // 'Database connection failed'
}
Best Practices
- ā¢Use specific error types - Don't just throw generic
Error - ā¢Include context - Add relevant data to error objects
- ā¢Preserve stack trace - Use
Error.captureStackTracewhen extending - ā¢Chain errors - Use
causeto preserve original error - ā¢Document error codes - Use consistent error codes
- ā¢Don't catch and ignore - Either handle or re-throw
Summary
| Concept | Description |
|---|---|
| Built-in errors | SyntaxError, TypeError, ReferenceError, etc. |
| Error properties | name, message, stack, cause |
| Custom errors | Extend Error class, add properties |
| Error hierarchy | Create base AppError, extend for specific types |
| Error cause | Chain errors with { cause: originalError } |