Complete JavaScript Course Module 11: Intermediate: Useful Built-in Features

Error Handling in JavaScript: try, catch and finally

  • Beginner
  • Lesson 11.3
  • 5 min read
  • 49 of 76 in the course

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 typeWhat it usually meansExample
ReferenceErrorYou used a variable that does not existconsole.log(totl)
TypeErrorYou used a value in the wrong waynull.toUpperCase()
SyntaxErrorThe code or data is not written correctlyJSON.parse("{name: Ana}")
RangeErrorA number is outside the allowed rangenew 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 no name, message or stack trace. Always throw an Error object (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 catch block. An empty catch {} 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, SyntaxError and RangeError; each has a name and message.
  • Read a stack trace from the top and look for the first line that points to your file.
  • try runs risky code, catch handles the error, and finally always 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.