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...finallyexecution flow - Implement custom
errorthrowing using thethrowstatement - Utilize JavaScript's built-in
Errorobject to diagnose failures - Apply nested
try...catchblocks 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
tryblock runs.- If no error → skips
catch→ runsfinally. - If error occurs → jumps to
catch→ runscatch→ runsfinally.
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
<!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
<!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.
- 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.
- 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
<!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
<!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>