CodingNic

Error Handling

try, catch, and finally

Error Handling 15 min read

try, catch, and finally

Objectives

By the end of this lesson, you should be able to:

  • Explain what happens when an error is thrown and nothing catches it
  • Wrap risky code in try/catch and read the error’s message
  • Use finally to run cleanup code whether or not an error happened

💡 Why this matters: Right now, one bad input can crash your entire program. A user pastes malformed data, a network response isn’t what you expected, a method gets called on something that turns out to be undefined, and the whole script stops. try/catch lets you decide what happens next instead of letting JavaScript decide for you.

An Error With Nothing to Catch It

Here’s a small program with a bug: it calls sayHello() on an object that doesn’t have that method.

javascript
const user = { name: "Priya" };
user.sayHello();
console.log("This never runs");

Running this crashes immediately:

text
TypeError: user.sayHello is not a function

Notice the last line, console.log("This never runs"), never runs. Once an error is thrown and nothing catches it, JavaScript stops executing the rest of the script right there.

try and catch

Wrap the risky line in a try block, and put a catch block right after it. If anything inside try throws an error, execution jumps straight to catch instead of crashing the program.

javascript
const user = { name: "Priya" };

try {
  user.sayHello();
} catch (error) {
  console.log("Caught an error:", error.message);
}

console.log("The program keeps running");

Output:

text
Caught an error: user.sayHello is not a function
The program keeps running

The error object passed into catch always has a .message property describing what went wrong. Because the error was caught, the line after the try/catch block runs normally.

A Second Example: JSON.parse

JSON.parse is a common source of real runtime errors: it throws whenever the text it’s given isn’t valid JSON.

javascript
const data = "{ bad json";

try {
  const parsed = JSON.parse(data);
  console.log(parsed);
} catch (error) {
  console.log("Caught an error:", error.message);
}

Output:

text
Caught an error: Expected property name or '}' in JSON at position 2 (line 1 column 3)

The exact wording of error.message can vary slightly between JavaScript engines, but the important part is the same everywhere: the program didn’t crash, and you got a description of what went wrong.

finally: Code That Always Runs

A finally block runs after try/catch, no matter what happened: whether the code succeeded, or an error was thrown and caught. It’s the right place for cleanup work, like closing a connection or logging that an operation finished.

javascript
function parseConfig(text) {
  try {
    const config = JSON.parse(text);
    console.log("Parsed config:", config);
    return config;
  } catch (error) {
    console.log("Failed to parse config:", error.message);
    return null;
  } finally {
    console.log("finally: parseConfig is done running");
  }
}

console.log("--- valid input ---");
parseConfig('{"theme": "dark"}');

console.log("--- invalid input ---");
parseConfig("{ not valid json");

Output:

text
--- valid input ---
Parsed config: { theme: 'dark' }
finally: parseConfig is done running
--- invalid input ---
Failed to parse config: Expected property name or '}' in JSON at position 2 (line 1 column 3)
finally: parseConfig is done running

Look at the last line of each block: finally: parseConfig is done running prints both times, once after the successful parse and once after the failed one. That’s the whole point of finally: it doesn’t care whether try succeeded or catch ran, it runs either way.

Try It

Predict the output first, then run this in your own console (browser devtools or node) to check:

javascript
function divide(a, b) {
  try {
    if (b === 0) {
      throw new Error("Cannot divide by zero");
    }
    console.log("Result:", a / b);
  } catch (error) {
    console.log("Error:", error.message);
  } finally {
    console.log("divide() finished");
  }
}

divide(10, 2);
divide(10, 0);

Check that divide() finished prints after both calls, even though only the second one hits the catch block.

Recap

  • An uncaught error stops your entire program; nothing after it runs.
  • try wraps risky code; catch (error) runs if that code throws, and error.message describes what happened.
  • finally always runs after try/catch, whether or not an error occurred, making it the right spot for cleanup.

Next lesson, you’ll raise your own errors on purpose with throw, and build a custom error class so callers can tell exactly what kind of problem occurred.