JS: Error Object

By Xah Lee. Date: . Last updated: .
xtodo

2015-08-13 todo, work in progress .

JavaScript error object, is typically use with the “throw” statement. [see Throw Try Catch Finally]

Error.prototype

Error()

Error(message)message is a optional message for information purposes.

new Error() → same as Error().

Properties

message → value is from the argument when Error(...) is called. If none given, it's supplied by browser.

name → a name for the error.

xtodo

JavaScript Error Object Tutorial (AI generated)

The Error object in JavaScript is the foundation for error handling. It represents runtime errors or exceptions that occur during program execution. Understanding it is essential for robust, debuggable code.

1. What is the Error Object?

When something goes wrong, JavaScript creates an Error instance (or a subclass). You can throw and catch these objects.

try {
  throw new Error("Something went wrong!");
} catch (err) {
  console.log(err); // Error object
}

2. Creating an Error

// Basic constructor
const err = new Error("This is a custom error message");

// With cause (modern browsers/Node.js)
const err2 = new Error("Failed to fetch", { cause: originalError });

3. Core Properties

Every Error instance has these key properties:

Property Description Example
message The error description "File not found"
name Type of error (default: "Error") "TypeError"
stack Call stack trace (non-standard but widely supported) Multi-line string with file/line
cause Underlying error (ES2022+) Another Error object
const err = new Error("Cannot read property 'x' of undefined");
console.log(err.name);     // "Error"
console.log(err.message);  // "Cannot read property 'x' of undefined"
console.log(err.stack);    // Full stack trace

Note: stack is not part of the ECMAScript standard but is implemented in all major engines (V8, SpiderMonkey, JavaScriptCore).

4. Built-in Error Subclasses

JavaScript provides several specialized error types:

try {
  null.toString();           // TypeError
} catch (e) {
  console.log(e.name);       // "TypeError"
}

5. Throwing Errors

You can throw any value, but you should always throw an Error instance for proper stack traces.

function divide(a, b) {
  if (b === 0) {
    throw new Error("Division by zero");
    // Or more specific:
    // throw new RangeError("Division by zero");
  }
  return a / b;
}

Best practice: Throw Error (or subclasses) instead of strings/numbers.

6. Catching and Handling Errors

try {
  riskyOperation();
} catch (error) {
  if (error instanceof TypeError) {
    console.error("Type error occurred:", error.message);
  } else if (error instanceof ReferenceError) {
    console.error("Reference error:", error.message);
  } else {
    console.error("Unknown error:", error);
  }

  // Log stack in development
  if (process?.env?.NODE_ENV === "development") {
    console.error(error.stack);
  }
} finally {
  // Always runs (cleanup)
  console.log("Cleanup done");
}

Modern pattern – Using cause and structured errors:

try {
  await fetchData();
} catch (error) {
  throw new Error("Failed to load user data", { cause: error });
}

7. Custom Error Classes (Recommended)

Since ES6, create your own error types with proper inheritance:

class ValidationError extends Error {
  constructor(message, field) {
    super(message);
    this.name = "ValidationError";
    this.field = field;
  }
}

class DatabaseError extends Error {
  constructor(message, originalError) {
    super(message, { cause: originalError });
    this.name = "DatabaseError";
  }
}

// Usage
function validateUser(user) {
  if (!user.email) {
    throw new ValidationError("Email is required", "email");
  }
}

Check error type:

try {
  validateUser({});
} catch (err) {
  if (err instanceof ValidationError) {
    console.log(`Validation failed on ${err.field}`);
  }
}

8. Global Error Handling

Browser:

window.addEventListener("error", (event) => {
  console.log("Uncaught error:", event.error);
});

window.addEventListener("unhandledrejection", (event) => {
  console.log("Unhandled promise rejection:", event.reason);
});

Node.js:

process.on("uncaughtException", (err) => {
  console.error("Uncaught exception:", err);
  process.exit(1);
});

process.on("unhandledRejection", (reason) => {
  console.error("Unhandled rejection:", reason);
});

9. Common Patterns and Best Practices

  1. Never swallow errors silently – Always log or rethrow.

  2. Use specific error types when possible.

  3. Add context to messages.

  4. Async error handling:

    async function fetchData() {
      try {
        const res = await fetch("/api/data");
        if (!res.ok) throw new Error(`HTTP ${res.status}`);
        return await res.json();
      } catch (err) {
        console.error("Fetch failed:", err);
        throw err; // rethrow or wrap
      }
    }
  5. Error boundaries in React (component level).

  6. Avoid eval() and dangerous constructs that produce hard-to-catch SyntaxError.

10. Debugging Tips

Quick Reference Cheat Sheet

// Create
new Error(message, { cause });

// Throw
throw new TypeError("...");

// Check
if (error instanceof Error) { ... }

// Custom
class MyError extends Error {
  constructor(msg) { super(msg); this.name = "MyError"; }
}

The Error object is simple yet powerful. Mastering it turns "random crashes" into predictable, debuggable failures.

Further Reading:

Practice by building a small utility that wraps all your async functions with consistent error logging! Let me know if you want a deeper dive into any section (e.g., error handling in async/await, libraries like neverthrow, or Node.js specifics).

JavaScript. Try, Catch, Throw, Error