Skip to main content

JavaScript Error Objects

Introduction

When you're writing JavaScript code, things don't always go as planned. Data might be in an unexpected format, network requests can fail, or you might have made a mistake in your code. JavaScript provides Error objects as a structured way to handle these situations.

Error objects are a fundamental part of JavaScript's error handling mechanism. They encapsulate information about what went wrong, where it happened, and sometimes even why. Understanding how to work with these objects is essential for writing robust, maintainable code that can gracefully handle unexpected situations.

What are Error Objects?

An Error object in JavaScript is an instance of the Error constructor. When something goes wrong in your code, JavaScript creates one of these objects containing information about the error that occurred.

Here's the basic syntax for creating an Error object:

javascript
const error = new Error(message);

The message parameter is a human-readable description of the error. It's optional, but it's good practice to include a meaningful message.

Properties of Error Objects

Every Error object has the following standard properties:

1. message

The message property contains a description of the error:

javascript
const error = new Error("Something went wrong!");
console.log(error.message); // Output: "Something went wrong!"

2. name

The name property specifies the type of error:

javascript
const error = new Error("Database connection failed");
console.log(error.name); // Output: "Error"

For the base Error constructor, the name property is set to "Error". For other error types (which we'll explore shortly), the name property will reflect the specific error type.

3. stack

The stack property contains a stack trace - a textual representation of the call stack at the point where the error was created:

javascript
function firstFunction() {
secondFunction();
}

function secondFunction() {
thirdFunction();
}

function thirdFunction() {
const error = new Error("An error occurred!");
console.log(error.stack);
}

firstFunction();

// Output might look like:
// Error: An error occurred!
// at thirdFunction (file.js:10:17)
// at secondFunction (file.js:6:3)
// at firstFunction (file.js:2:3)
// at file.js:14:1

The stack trace is incredibly useful for debugging as it shows the path of execution that led to the error.

Built-in Error Types

JavaScript has several built-in error types that extend the base Error object. Each specializes in representing a different category of error:

1. SyntaxError

Occurs when there's an error in the syntax of your code:

javascript
try {
eval("console.log('Hello world'"); // Missing closing parenthesis
} catch (error) {
console.log(error.name); // Output: "SyntaxError"
console.log(error.message); // Output will describe the syntax issue
}

2. ReferenceError

Occurs when you try to reference a variable that doesn't exist:

javascript
try {
console.log(undefinedVariable); // This variable doesn't exist
} catch (error) {
console.log(error.name); // Output: "ReferenceError"
console.log(error.message); // Output: "undefinedVariable is not defined"
}

3. TypeError

Occurs when a value is not of the expected type:

javascript
try {
const num = 123;
num.toUpperCase(); // Numbers don't have this method
} catch (error) {
console.log(error.name); // Output: "TypeError"
console.log(error.message); // Output: "num.toUpperCase is not a function"
}

4. RangeError

Occurs when a value is outside the allowable range:

javascript
try {
const arr = new Array(-1); // Array size can't be negative
} catch (error) {
console.log(error.name); // Output: "RangeError"
console.log(error.message); // Output will describe the range issue
}

5. URIError

Occurs when using global URI handling functions incorrectly:

javascript
try {
decodeURI('%'); // % should be followed by two hex digits
} catch (error) {
console.log(error.name); // Output: "URIError"
console.log(error.message); // Output will describe the URI issue
}

6. EvalError

Historically used for errors in the global eval() function, but in modern JavaScript, SyntaxError, ReferenceError, or TypeError are thrown instead.

Creating Custom Error Types

Sometimes the built-in error types don't fully capture the nature of an error in your application. In these cases, you can create custom error types by extending the Error class:

javascript
class DatabaseError extends Error {
constructor(message, code) {
super(message);
this.name = "DatabaseError";
this.code = code;
}
}

try {
throw new DatabaseError("Failed to connect to database", "DB_CONN_ERROR");
} catch (error) {
console.log(error.name); // Output: "DatabaseError"
console.log(error.message); // Output: "Failed to connect to database"
console.log(error.code); // Output: "DB_CONN_ERROR"
}

Custom error types allow you to add application-specific properties and methods to your errors, making them more descriptive and useful.

Practical Error Handling Examples

Let's look at some practical examples of using Error objects in real-world scenarios.

Example 1: Form Validation

javascript
function validateUsername(username) {
if (!username) {
throw new Error("Username cannot be empty");
}

if (username.length < 3) {
throw new Error("Username must be at least 3 characters long");
}

if (!/^[a-zA-Z0-9_]+$/.test(username)) {
throw new Error("Username can only contain letters, numbers, and underscores");
}

return true;
}

try {
validateUsername("user@name");
} catch (error) {
console.log(error.message); // Output: "Username can only contain letters, numbers, and underscores"
// Display error message to the user in the UI
}

Example 2: API Requests with Custom Errors

javascript
class ApiError extends Error {
constructor(message, status) {
super(message);
this.name = "ApiError";
this.status = status;
}
}

async function fetchUserData(userId) {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);

if (!response.ok) {
throw new ApiError(`Failed to fetch user data: ${response.statusText}`, response.status);
}

return await response.json();
} catch (error) {
if (error instanceof ApiError) {
console.error(`API Error (${error.status}): ${error.message}`);
// Handle specific status codes
if (error.status === 404) {
// Handle not found
} else if (error.status === 401) {
// Handle unauthorized
}
} else {
console.error("Network or other error:", error.message);
}
throw error; // Rethrow to let callers handle it
}
}

// Using the function
fetchUserData(123)
.then(data => {
// Process user data
})
.catch(error => {
// Handle error at a higher level
});

Example 3: Error Handling in a Node.js Application

javascript
const fs = require('fs').promises;

class FileSystemError extends Error {
constructor(message, operation, filePath) {
super(message);
this.name = "FileSystemError";
this.operation = operation;
this.filePath = filePath;
}
}

async function readConfigFile(path) {
try {
const data = await fs.readFile(path, 'utf8');
return JSON.parse(data);
} catch (error) {
if (error.code === 'ENOENT') {
throw new FileSystemError(
`Config file not found: ${path}`,
'read',
path
);
} else if (error instanceof SyntaxError) {
throw new FileSystemError(
`Invalid JSON in config file: ${error.message}`,
'parse',
path
);
}
throw error;
}
}

// Usage
readConfigFile('config.json')
.then(config => {
// Use the configuration
})
.catch(error => {
if (error instanceof FileSystemError) {
console.error(`${error.name}: ${error.message}`);
console.error(`Operation: ${error.operation}, Path: ${error.filePath}`);

// Specific handling based on the operation
if (error.operation === 'read') {
// Create a default config file
} else if (error.operation === 'parse') {
// Alert admin about corrupted config
}
} else {
console.error('Unexpected error:', error);
}
});

Summary

JavaScript Error objects are a powerful mechanism for handling and communicating errors in your code. In this guide, we've explored:

  • The basic structure of Error objects and their properties (message, name, stack)
  • Built-in error types like SyntaxError, ReferenceError, and TypeError
  • How to create custom error types for application-specific errors
  • Practical examples of error handling in different contexts

Effective use of Error objects leads to more robust, maintainable code that can gracefully handle unexpected situations. By creating descriptive error messages and leveraging the stack trace, you can make debugging easier. Custom error types allow you to add context-specific information that can help diagnose and recover from errors.

Additional Resources

Exercises

  1. Create a custom ValidationError class that accepts a field name and a validation rule that failed.
  2. Write a function that reads data from localStorage and handles different potential errors (item not found, storage quota exceeded, etc.).
  3. Implement a "retry" mechanism that catches specific errors and retries an operation a limited number of times before giving up.
  4. Create a logging utility that formats and logs different types of errors in a consistent way.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)