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:
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 informationAll error objects have these three core properties:
name: Error type namemessage: Error descriptionstack: 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:
console.log(nonExistentVariable); // ReferenceError: nonExistentVariable is not definedThis is one of the most common errors, usually caused by:
1. Variable Spelling Mistakes
let userName = "Alice";
console.log(usrName); // ReferenceError: usrName is not defined2. Variable Scope Issues
function test() {
let localVar = "I'm local";
}
test();
console.log(localVar); // ReferenceError: localVar is not defined3. Accessing Variables Before Declaration (for let and const)
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:
console.log(myVar); // undefined (not ReferenceError)
var myVar = 10;Handling ReferenceError
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
let value = 42;
value(); // TypeError: value is not a function
let obj = { name: "Alice" };
obj.method(); // TypeError: obj.method is not a function2. Accessing Properties of null or undefined
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 undefinedThis is a very common error, usually requiring checking if the value exists first:
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
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 allowed4. Calling Non-existent Methods
let num = 123;
num.toUpperCase(); // TypeError: num.toUpperCase is not a functionHandling TypeError
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:
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:
// 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 JSONMost SyntaxErrors are detected when the code is loaded, not at runtime. But dynamically executed code (like eval or JSON.parse) might throw at runtime:
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
// ❌ 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
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)
function recursiveFunction() {
recursiveFunction(); // Infinite recursion
}
recursiveFunction(); // RangeError: Maximum call stack size exceeded3. Invalid Parameters for Number Methods
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 100Handling RangeError
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:
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 malformedThis 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).
// 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:
// Situations that might cause InternalError (Firefox only)
// - Too many switch cases
// - Overly complex regular expressions
// - Excessively large array initializersSince 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:
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:
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
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
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
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
try {
someRiskyOperation();
} catch (error) {
if (error instanceof TypeError) {
// Handle type error
} else if (error instanceof ReferenceError) {
// Handle reference error
}
}Check error.name
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
// ❌ 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
instanceofto check error types - Adopt different handling strategies based on error types
- Don't rely on
error.messagefor judgment
Mastering these error types allows you to identify problems faster during debugging and write more robust error handling logic during coding.