Skip to main content

Session 7

Javascript Error Handling

During script execution, errors are inevitable. They can stem from syntax mistakes, invalid user input, network failures, or unforeseen runtime conditions. Without proper error handling, a single failure can crash an entire application or leave users with broken interfaces. This session introduces JavaScript's structured error-handling mechanisms, enabling developers to gracefully catch, diagnose, and recover from runtime exceptions while keeping applications stable and user-friendly.

Objectives

  • Understand the try...catch...finally execution flow
  • Implement custom error throwing using the throw statement
  • Utilize JavaScript's built-in Error object to diagnose failures
  • Apply nested try...catch blocks for complex validation scenarios
  • Practice robust error handling through hands-on lab exercises

Error Handling Syntax

JavaScript uses a structured block system to manage exceptions:

		try {
  // Code that might throw an error
} catch(error) {
  // Executes ONLY if an error occurs in try
  // `error` is an instance of the built-in Error object
} finally {
  // ALWAYS executes, regardless of success or failure
  // Ideal for cleanup, closing connections, resetting UI
}
	

Execution Flow

  1. try block runs.
  2. If no error → skips catch → runs finally.
  3. If error occurs → jumps to catch → runs catch → runs finally.

The throw Statement

Allows you to create custom errors or re-throw caught errors:

		throw "Email cannot be empty";      // Throws a string
throw 404;                          // Throws a number
throw new Error("Custom validation failed"); // Throws an Error object (recommended)
	

Built-in Error Object

When an error is caught, JavaScript provides an error object with two key properties:

Property Description
error.name Type of error (e.g., "TypeError", "ReferenceError")
error.message Human-readable description of what went wrong

Error Types

Error Name Trigger Condition
SyntaxError Invalid JS syntax (e.g., missing brackets, 2 = x)
ReferenceError Accessing an undeclared variable
TypeError Invalid operation on a value type (e.g., "abc".toUpperCase on undefined)
RangeError Number outside valid range (e.g., invalid array length)
EvalError Error inside eval() function
URIError Malformed URI in encoding/decoding functions

Nested try...catch Blocks

For complex validation, you can nest blocks to handle errors at different levels:

		try {
  // Outer validation
  try {
    // Inner risky operation
    JSON.parse(invalidJSON);
  } catch(innerErr) {
    console.warn("Inner failed:", innerErr.message);
    throw new Error("Data parsing failed"); // Re-throw to outer catch
  }
} catch(outerErr) {
  console.error("Outer caught:", outerErr.message);
} finally {
  console.log("Cleanup complete");
}
	

Best Practices & Common Pitfalls

Pitfall Best Practice
Empty catch {} blocks Always log or handle the error; never silently swallow failures
Throwing non-Error values Use throw new Error("msg") for consistent stack traces
Overusing try...catch for control flow Use conditionals (if/else) for expected logic; reserve try...catch for true exceptions
Ignoring finally for cleanup Use finally to reset UI states, close streams, or hide loading spinners
Assuming all errors are recoverable Some errors (e.g., SyntaxError) should be fixed at development time, not caught at runtime

Quick Guide

		// 🔍 Basic Error Handling
try { riskyOperation(); }
catch(err) { console.error(err.name, err.message); }
finally { console.log("Runs no matter what"); }
 
// 🚀 Custom Error
if (!userInput) throw new TypeError("Input is missing");
 
// 🔄 Retry Logic (Lab Exercise 4 pattern)
function safeMultiply(a, b) {
  while (true) {
    try { return primitiveMultiply(a, b); }
    catch (e) { if (e instanceof MultiplicatorUnitFailure) continue; else throw e; }
  }
}
	

Question 1

Problem Statement

Write an HTML code using JavaScript to add two numbers taken as input from user and display the result of division operation on them i.e. a/b. If an error occurs, show the error. Use error handling concepts in JavaScript.

Preview

Divide a / b

Waiting for input...

Code

add-division.html
		<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Ex1: Division Error Handling</title>
        <style>
            body {
                font-family: sans-serif;
                padding: 20px;
                max-width: 400px;
            }
        </style>
    </head>
    <body>
        <h2>Divide a / b</h2>
        <input
            id="numA"
            type="number"
            placeholder="Number a"
            style="width: 100%; margin-bottom: 8px; padding: 5px"
        />
        <input
            id="numB"
            type="number"
            placeholder="Number b"
            style="width: 100%; margin-bottom: 8px; padding: 5px"
        />
        <button id="calcBtn" style="padding: 8px 16px">Calculate</button>
        <p id="output" style="margin-top: 10px; font-weight: bold"></p>
 
        <script>
            document.getElementById("calcBtn").addEventListener("click", () => {
                try {
                    const a = parseFloat(document.getElementById("numA").value);
                    const b = parseFloat(document.getElementById("numB").value);
 
                    if (isNaN(a) || isNaN(b))
                        throw new TypeError(
                            "Both fields must be valid numbers.",
                        );
                    if (b === 0)
                        throw new RangeError("Division by zero is undefined.");
 
                    document.getElementById("output").textContent =
                        `Result: ${a / b}`;
                } catch (err) {
                    document.getElementById("output").textContent =
                        `❌ Error (${err.name}): ${err.message}`;
                }
            });
        </script>
    </body>
</html>
	

HTML View

Question 2

Problem Statement

A teacher has created a gradeLabs function that verifies if student programming labs work. This function loops over an array of JavaScript objects that should contain a student property and runLab property. The runLab property is expected to be a function containing the student's code. The runLab function is called and the result is compared to the expected result. If the result and expected result don't match, then the lab is considered a failure.

Demo

Auto-Grader

Code

grade-lab.html
		<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Ex2: Grade Labs</title>
        <style>
            body {
                font-family: sans-serif;
                padding: 20px;
            }
            pre {
                background: #f4f4f4;
                padding: 10px;
                border-radius: 5px;
            }
        </style>
    </head>
    <body>
        <h2>Auto-Grader</h2>
        <button id="gradeBtn">Run gradeLabs()</button>
        <pre id="results"></pre>
 
        <script>
            const students = [
                { name: "Alice", runLab: () => 2 + 2, expected: 4 },
                {
                    name: "Bob",
                    runLab: () => {
                        throw new ReferenceError("x is not defined");
                    },
                    expected: 5,
                },
                { name: "Charlie", runLab: () => 10 / 2, expected: 6 }, // Wrong result
                { name: "Diana", runLab: () => "5" * 2, expected: 10 }, // Type coercion success
            ];
 
            function gradeLabs(students) {
                let logs = [];
                for (const s of students) {
                    try {
                        const actual = s.runLab();
                        if (actual === s.expected)
                            logs.push(`✅ ${s.name}: PASS`);
                        else
                            logs.push(
                                `❌ ${s.name}: FAIL (Got ${actual}, Expected ${s.expected})`,
                            );
                    } catch (err) {
                        logs.push(
                            `⚠️ ${s.name}: EXCEPTION - ${err.name}: ${err.message}`,
                        );
                    }
                }
                return logs.join("\n");
            }
 
            document
                .getElementById("gradeBtn")
                .addEventListener("click", () => {
                    document.getElementById("results").textContent =
                        gradeLabs(students);
                });
        </script>
    </body>
</html>
	

HTML View

Question 3

Problem Statement

There is an array of animals. The user is asked to enter the index for the animal they want to see.

  1. If the user enters an index that does NOT contain an animal, the code will throw a TypeError when name is referenced on an undefined value.
  2. Update the above program to print out the index the user entered. We want this message to be printed EVERY time the code runs

Demo

Animal Lookup

Code

animals.html
		<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Ex3: Animal Lookup</title>
        <style>
            body {
                font-family: sans-serif;
                padding: 20px;
            }
            input,
            button {
                padding: 8px;
                margin-right: 5px;
            }
        </style>
    </head>
    <body>
        <h2>Animal Lookup</h2>
        <input id="idxInput" type="number" placeholder="Enter index (0-2)" />
        <button id="lookupBtn">Find Animal</button>
        <p id="animalRes" style="margin-top: 10px; font-weight: bold"></p>
        <p id="finallyMsg" style="color: #555"></p>
 
        <script>
            const animals = [
                { id: 0, name: "Lion" },
                { id: 1, name: "Elephant" },
                { id: 2, name: "Tiger" },
            ];
 
            document
                .getElementById("lookupBtn")
                .addEventListener("click", () => {
                    const idx = parseInt(
                        document.getElementById("idxInput").value,
                    );
                    let animalName = "";
 
                    try {
                        const animal = animals[idx];
                        if (!animal)
                            throw new TypeError(
                                `Index ${idx} does not contain an animal.`,
                            );
                        animalName = animal.name;
                        document.getElementById("animalRes").textContent =
                            `🦁 Animal: ${animalName}`;
                    } catch (err) {
                        document.getElementById("animalRes").textContent =
                            `❌ ${err.message}`;
                    } finally {
                        document.getElementById("finallyMsg").textContent =
                            `📝 Checked index: ${idx} (Executed via finally)`;
                    }
                });
        </script>
    </body>
</html>
	

HTML View

Question 4

Problem Statement

Assume you have a function primitiveMultiply that in 20 percent of cases multiplies two numbers and in the other 80 percent of cases raises an exception of type MultiplicatorUnitFailure. Write a function that wraps this clunky function and just keeps trying until a call succeeds, after which it returns the result. Make sure you handle only the exceptions you are trying to handle.

Demo

Reliable Multiply

Input: 6 × 7 (20% success rate per attempt)

Code

primitive-multiplyn.html
		<!doctype html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Ex4: Retry Multiplication</title>
        <style>
            body {
                font-family: sans-serif;
                padding: 20px;
            }
            button {
                padding: 10px 20px;
            }
        </style>
    </head>
    <body>
        <h2>Reliable Multiply</h2>
        <p>Input: <strong>6 × 7</strong></p>
        <button id="multiplyBtn">Run Wrapper</button>
        <p id="res" style="margin-top: 10px; font-weight: bold"></p>
 
        <script>
            class MultiplicatorUnitFailure extends Error {
                constructor(msg) {
                    super(msg);
                    this.name = "MultiplicatorUnitFailure";
                }
            }
 
            function primitiveMultiply(a, b) {
                if (Math.random() < 0.2) return a * b;
                else
                    throw new MultiplicatorUnitFailure(
                        "Random failure (80% chance)",
                    );
            }
 
            function reliableMultiply(a, b) {
                let attempts = 0;
                while (true) {
                    attempts++;
                    try {
                        return { result: primitiveMultiply(a, b), attempts };
                    } catch (e) {
                        if (!(e instanceof MultiplicatorUnitFailure)) throw e; // Re-throw unknown errors
                    }
                }
            }
 
            document
                .getElementById("multiplyBtn")
                .addEventListener("click", () => {
                    const { result, attempts } = reliableMultiply(6, 7);
                    document.getElementById("res").textContent =
                        `✅ Result: ${result} (Took ${attempts} attempt${attempts > 1 ? "s" : ""})`;
                });
        </script>
    </body>
</html>
	

HTML View