Skip to content

Error Types: Understanding JavaScript's Built-in Errors

In medicine, doctors need to accurately diagnose the type of illness—whether it's a cold, pneumonia, or an allergy—to prescribe the right treatment. Similarly, in programming, understanding different types of errors helps you quickly identify problems and adopt the correct repair strategies. JavaScript provides multiple built-in error types, each representing specific categories of problems.

Error: The Base Class of All Errors

Error is the parent class of all built-in error types. It can be used directly or as the base class for custom errors:

javascript
let error = new Error("Something went wrong");

console.log(error.name); // "Error"
console.log(error.message); // "Something went wrong"
console.log(error.stack); // Call stack information

All error objects have these three core properties:

  • name: Error type name
  • message: Error description
  • stack: Call stack when the error occurred (for debugging)

While Error can be used directly, more specific error types provide more information. Let's understand each of them.

ReferenceError: Reference Errors

ReferenceError is thrown when you try to access a variable that doesn't exist:

javascript
console.log(nonExistentVariable); // ReferenceError: nonExistentVariable is not defined

This is one of the most common errors, usually caused by:

1. Variable Spelling Mistakes

javascript
let userName = "Alice";
console.log(usrName); // ReferenceError: usrName is not defined

2. Variable Scope Issues

javascript
function test() {
  let localVar = "I'm local";
}

test();
console.log(localVar); // ReferenceError: localVar is not defined

3. Accessing Variables Before Declaration (for let and const)

javascript
console.log(myVar); // ReferenceError: Cannot access 'myVar' before initialization
let myVar = 10;

This is called the "Temporal Dead Zone" (TDZ). Variables declared with let and const cannot be accessed before declaration, while var is hoisted and returns undefined:

javascript
console.log(myVar); // undefined (not ReferenceError)
var myVar = 10;

Handling ReferenceError

javascript
try {
  console.log(possiblyUndefined);
} catch (error) {
  if (error instanceof ReferenceError) {
    console.log("Variable does not exist");
    // Use default value or take other action
  }
}

TypeError: Type Errors

TypeError is thrown when you perform incompatible operations on a value, such as calling a non-function as a function, or accessing properties of null/undefined:

1. Calling Non-functions

javascript
let value = 42;
value(); // TypeError: value is not a function

let obj = { name: "Alice" };
obj.method(); // TypeError: obj.method is not a function

2. Accessing Properties of null or undefined

javascript
let user = null;
console.log(user.name); // TypeError: Cannot read property 'name' of null

let data = undefined;
console.log(data.length); // TypeError: Cannot read property 'length' of undefined

This is a very common error, usually requiring checking if the value exists first:

javascript
let user = null;

// ✅ Safe access
if (user) {
  console.log(user.name);
} else {
  console.log("User is null");
}

// ✅ Using optional chaining (ES2020+)
console.log(user?.name); // undefined (doesn't throw error)

3. Assigning to Immutable Values

javascript
const PI = 3.14;
PI = 3.14159; // TypeError: Assignment to constant variable

const obj = { name: "Alice" };
obj = { name: "Bob" }; // TypeError: Assignment to constant variable

// But you can modify object properties
obj.name = "Bob"; // ✅ This is allowed

4. Calling Non-existent Methods

javascript
let num = 123;
num.toUpperCase(); // TypeError: num.toUpperCase is not a function

Handling TypeError

javascript
function getUserName(user) {
  try {
    return user.name.toUpperCase();
  } catch (error) {
    if (error instanceof TypeError) {
      console.log("Invalid user object");
      return "Unknown";
    }
    throw error;
  }
}

console.log(getUserName(null)); // Invalid user object, returns "Unknown"
console.log(getUserName({ name: "Alice" })); // "ALICE"

A better approach is to check beforehand:

javascript
function getUserName(user) {
  if (!user || !user.name) {
    return "Unknown";
  }

  if (typeof user.name !== "string") {
    return "Unknown";
  }

  return user.name.toUpperCase();
}

SyntaxError: Syntax Errors

SyntaxError indicates syntax problems in the code that the JavaScript engine cannot parse:

javascript
// Missing closing bracket
function test( {
  console.log("hello");
}

// Illegal object literal
let obj = { a: 1, b: };

// Illegal JSON
JSON.parse('{ invalid json }'); // SyntaxError: Unexpected token i in JSON

Most SyntaxErrors are detected when the code is loaded, not at runtime. But dynamically executed code (like eval or JSON.parse) might throw at runtime:

javascript
try {
  eval("let x = ;"); // SyntaxError: Unexpected token ';'
} catch (error) {
  if (error instanceof SyntaxError) {
    console.log("Invalid syntax");
  }
}

try {
  JSON.parse('{ "name": "Alice" }'); // ✅ Valid JSON
  JSON.parse("{ name: 'Alice' }"); // SyntaxError: Invalid JSON (keys must be in double quotes)
} catch (error) {
  if (error instanceof SyntaxError) {
    console.log("Invalid JSON format");
  }
}

Common SyntaxError Scenarios

javascript
// ❌ Unexpected identifier
let name = John; // SyntaxError: John is not defined (should be a string)
let name = "John"; // ✅

// ❌ Unexpected token
let arr = [1, 2, 3,]; // Might error in older browsers (trailing comma)
let arr = [1, 2, 3];  // ✅

// ❌ Missing semicolon or comma
let obj = {
  name: "Alice"
  age: 30  // SyntaxError: Missing comma
};

RangeError: Range Errors

RangeError is thrown when a value is outside the valid range:

1. Invalid Array Length

javascript
let arr = new Array(-1); // RangeError: Invalid array length
let arr2 = new Array(4294967296); // RangeError: Invalid array length (too large)

Array length must be a non-negative integer and less than 2^32.

2. Deep Recursive Calls (Stack Overflow)

javascript
function recursiveFunction() {
  recursiveFunction(); // Infinite recursion
}

recursiveFunction(); // RangeError: Maximum call stack size exceeded

3. Invalid Parameters for Number Methods

javascript
let num = 123.456;
num.toFixed(-1); // RangeError: toFixed parameter must be between 0 and 100
num.toFixed(101); // RangeError

num.toPrecision(0); // RangeError: toPrecision parameter must be between 1 and 100

Handling RangeError

javascript
function safeToFixed(num, digits) {
  try {
    if (digits < 0 || digits > 100) {
      digits = 2; // Use default value
    }
    return num.toFixed(digits);
  } catch (error) {
    if (error instanceof RangeError) {
      console.log("Invalid precision, using default");
      return num.toFixed(2);
    }
    throw error;
  }
}

console.log(safeToFixed(123.456, -1)); // Invalid precision, returns "123.46"
console.log(safeToFixed(123.456, 3)); // "123.456"

URIError: URI Handling Errors

URIError is thrown when invalid parameters are passed to global URI handling functions:

javascript
try {
  decodeURIComponent("%"); // URIError: URI malformed
} catch (error) {
  if (error instanceof URIError) {
    console.log("Invalid URI component");
  }
}

// ✅ Valid URI encoding
let encoded = encodeURIComponent("Hello World"); // "Hello%20World"
let decoded = decodeURIComponent(encoded); // "Hello World"

// ❌ Invalid URI
decodeURIComponent("%E0%A4%A"); // URIError: URI malformed

This error is relatively uncommon and mainly encountered when handling URL encoding/decoding.

EvalError: Eval Errors (Non-standard)

EvalError is theoretically related to the eval() function, but is rarely used in modern JavaScript. In the current specification, errors from eval() typically throw other types of errors (like SyntaxError).

javascript
// Modern JavaScript doesn't throw EvalError
eval("let x = 1"); // ✅ Works normally

// Syntax errors throw SyntaxError
try {
  eval("let x = ;");
} catch (error) {
  console.log(error.name); // SyntaxError (not EvalError)
}

Therefore, you almost never need to handle EvalError.

InternalError: Internal Errors (Non-standard)

InternalError is a non-standard extension in some browsers (like Firefox), representing JavaScript engine internal errors:

javascript
// Situations that might cause InternalError (Firefox only)
// - Too many switch cases
// - Overly complex regular expressions
// - Excessively large array initializers

Since this is not a standard error, you shouldn't rely on it in cross-browser code.

AggregateError: Aggregate Errors (ES2021)

AggregateError is used to represent a collection of multiple errors, common when Promise.any() fails:

javascript
Promise.any([
  Promise.reject(new Error("Error 1")),
  Promise.reject(new Error("Error 2")),
  Promise.reject(new Error("Error 3")),
]).catch((error) => {
  console.log(error instanceof AggregateError); // true
  console.log(error.errors); // [Error: Error 1, Error: Error 2, Error: Error 3]
});

You can also create it manually:

javascript
let errors = [
  new Error("Failed to load user"),
  new Error("Failed to load posts"),
  new Error("Failed to load comments"),
];

throw new AggregateError(errors, "Multiple operations failed");

Practical Applications of Error Types

Different Strategies Based on Error Types

javascript
function handleError(error) {
  if (error instanceof TypeError) {
    console.log("Type error: check your data types");
    // Might be data format issues, try using default values
  } else if (error instanceof ReferenceError) {
    console.log("Reference error: variable does not exist");
    // Might be spelling errors or scope issues
  } else if (error instanceof RangeError) {
    console.log("Range error: value out of range");
    // Might be parameter validation issues
  } else if (error instanceof SyntaxError) {
    console.log("Syntax error: invalid code or data format");
    // Might be JSON parsing failure
  } else {
    console.log("Unknown error:", error.message);
  }
}

try {
  JSON.parse("{ invalid }");
} catch (error) {
  handleError(error); // Syntax error: invalid code or data format
}

Distinguishing Recoverable and Unrecoverable Errors

javascript
async function loadData() {
  try {
    let response = await fetch("/api/data");
    let data = await response.json();
    return processData(data);
  } catch (error) {
    if (error instanceof SyntaxError) {
      // JSON parsing failure: recoverable, use default data
      console.log("Invalid data format, using defaults");
      return getDefaultData();
    } else if (error instanceof TypeError) {
      // Type error: might be data structure issues, try to repair
      console.log("Data structure issue, attempting repair");
      return repairAndProcess(error);
    } else {
      // Other errors: unrecoverable, throw upward
      throw error;
    }
  }
}

Detailed Error Logging

javascript
function logError(error) {
  let errorInfo = {
    type: error.name,
    message: error.message,
    stack: error.stack,
    timestamp: new Date().toISOString(),
  };

  // Add extra information based on error type
  if (error instanceof RangeError) {
    errorInfo.category = "range-violation";
  } else if (error instanceof TypeError) {
    errorInfo.category = "type-mismatch";
  } else if (error instanceof ReferenceError) {
    errorInfo.category = "undefined-reference";
  }

  console.error("Error occurred:", errorInfo);
  // Send to logging service
  // sendToLoggingService(errorInfo);
}

Best Practices for Checking Error Types

Use instanceof

javascript
try {
  someRiskyOperation();
} catch (error) {
  if (error instanceof TypeError) {
    // Handle type error
  } else if (error instanceof ReferenceError) {
    // Handle reference error
  }
}

Check error.name

javascript
try {
  someRiskyOperation();
} catch (error) {
  switch (error.name) {
    case "TypeError":
      console.log("Type error occurred");
      break;
    case "ReferenceError":
      console.log("Reference error occurred");
      break;
    default:
      console.log("Unknown error:", error.name);
  }
}

Don't Rely on error.message

javascript
// ❌ Unreliable: error messages can change or differ
if (error.message.includes("not defined")) {
  // ...
}

// ✅ Use error types
if (error instanceof ReferenceError) {
  // ...
}

Summary

Understanding JavaScript's built-in error types helps you quickly identify problems and adopt the correct handling strategies. Each error type represents a specific category of problems, and recognizing them makes your error handling more precise and effective.

Key takeaways:

  • Error: Base class of all errors
  • ReferenceError: Accessing non-existent variables
  • TypeError: Type mismatch or incompatible operations on values
  • SyntaxError: Code syntax errors
  • RangeError: Values outside valid ranges
  • URIError: Invalid parameters for URI handling functions
  • AggregateError: Collection of multiple errors
  • Use instanceof to check error types
  • Adopt different handling strategies based on error types
  • Don't rely on error.message for judgment

Mastering these error types allows you to identify problems faster during debugging and write more robust error handling logic during coding.