In short
Errors happen in every program. This lesson explains JavaScript error handling: the common error types, how to read a stack trace, and how to use try, catch, finally and throw.
Errors happen in every program. This lesson explains JavaScript error handling: the common error types, how to read a stack trace, and how to use try, catch, finally and throw.
Common JavaScript error types
When something goes wrong, JavaScript creates an error object and stops running the current code. Each error has a name (its type) and a message (what went wrong). These are the types you will meet most often:
| Error type | What it usually means | Example |
|---|---|---|
ReferenceError | You used a variable that does not exist | console.log(totl) |
TypeError | You used a value in the wrong way | null.toUpperCase() |
SyntaxError | The code or data is not written correctly | JSON.parse("{name: Ana}") |
RangeError | A number is outside the allowed range | new Array(-1) |
How to read a stack trace
When an error is not handled, the console shows a red message with a stack trace. It is a list of the functions that were running, newest first, with file names and line numbers. Think of it as a trail of footprints leading back to where the problem started.
const getUser = () => null;
const showName = () => {
const user = getUser();
console.log(user.name);
};
showName();
Uncaught TypeError: Cannot read properties of null (reading 'name')
at showName (app.js:5:20)
at app.js:8:1
Read it from the top. The first line tells you the error type and message. The next line says the error happened inside showName, in app.js at line 5, column 20. The line below shows who called showName. Click the file link in the console to jump straight to that line. The exact wording differs a little between browsers, but the structure is the same.
Tip: Don't panic at red text. Read the first line, then find the first line in the trace that points to your file. That is usually where to look.
Using try, catch and finally
You can catch an error so your program keeps going. Put the risky code in a try block. If an error happens, JavaScript jumps to the catch block and gives you the error object. The optional finally block runs every time, whether there was an error or not, which is perfect for clean-up work.
const settingsText = '{"theme": "dark", "fontSize": 16';
let settings;
try {
settings = JSON.parse(settingsText);
console.log("Loaded settings");
} catch (error) {
console.log(`Could not read settings: ${error.name}`);
settings = { theme: "light", fontSize: 14 };
} finally {
console.log("Settings check complete");
}
console.log(settings.theme);
Could not read settings: SyntaxError
Settings check complete
light
The JSON text is missing its closing }, so JSON.parse throws a SyntaxError. Instead of crashing, we log a friendly message and use default settings. "Loaded settings" never prints, because JavaScript left the try block at the line that failed. error.message holds the detailed reason; its wording varies between browsers, so show your own text to users.
Throwing your own errors
You can create errors yourself with throw new Error("message"). This is useful when a function gets input it cannot work with. It is better to fail loudly with a clear message than to return a wrong result quietly.
const withdraw = (balance, amount) => {
if (typeof amount !== "number" || Number.isNaN(amount)) {
throw new TypeError("Amount must be a number");
}
if (amount <= 0) {
throw new RangeError("Amount must be greater than zero");
}
if (amount > balance) {
throw new Error(`Not enough money: you have $${balance}`);
}
return balance - amount;
};
const attempts = [30, 500, -5, "ten"];
for (const amount of attempts) {
try {
const newBalance = withdraw(100, amount);
console.log(`Success! New balance: $${newBalance}`);
} catch (error) {
console.log(`${error.name}: ${error.message}`);
}
}
Success! New balance: $70
Error: Not enough money: you have $100
RangeError: Amount must be greater than zero
TypeError: Amount must be a number
The function checks its input and throws a matching error type. The loop catches each one and reads error.name and error.message. Notice that one failed attempt does not stop the loop, because each call has its own try...catch.
Common mistake: Throwing a plain string, like
throw "Oops". A string has noname,messageor stack trace. Always throw anErrorobject (or one of its types).
When to catch errors and when to let them fail
Not every error should be caught. A good rule: catch an error only when you can do something useful about it.
Good reasons to catch:
- Reading data you do not control, like JSON from storage, a file or a server.
- Showing the user a friendly message instead of a broken page.
- Using a safe default value when something optional fails.
Reasons to let it fail:
- The error is a bug in your own code, like a typo in a variable name. Hiding it makes it harder to find. Fix the code instead.
- You have nothing sensible to do in the
catchblock. An emptycatch {}swallows the problem silently, and you will spend hours wondering why things do not work.
If you catch an error only to log it, use console.error(error) so it still stands out in red. You can learn more on MDN's Error reference.
Practice exercise
Write a function parseAge(text) that turns a string into an age. It should throw a TypeError if the text is not a number, and a RangeError if the age is below 0 or above 130. Test it in a loop with "25", "abc", "-3" and "200", catching each error and logging its name and message. Add a finally block that logs "Checked: " plus the input.
Hint: Number("abc") gives NaN, and Number.isNaN() checks for it.
Key takeaways
- Common error types are
ReferenceError,TypeError,SyntaxErrorandRangeError; each has anameandmessage. - Read a stack trace from the top and look for the first line that points to your file.
tryruns risky code,catchhandles the error, andfinallyalways runs.- Use
throw new Error("message")to stop with a clear reason when input is wrong. - Catch errors only when you can handle them; let real bugs fail so you can fix them.