Error Handling in JavaScript: Try, Catch, Finally
JavaScript applications rarely run perfectly all the time. A user may enter invalid input, an API request may fail, or a function may try to access something that does not exist. These situations create errors.
If errors are not handled properly, applications can crash, behave unpredictably, or become difficult to debug.
This is where JavaScript error handling becomes important.
In this blog, you will learn:
What errors are in JavaScript
How
tryandcatchblocks workThe purpose of the
finallyblockHow to throw custom errors
Why proper error handling matters
What Are Errors in JavaScript?
An error is a problem that interrupts the normal execution of a program.
For example:
console.log(user.name);
If user is not defined, JavaScript throws an error like this:
ReferenceError: user is not defined
The program stops executing at that point unless the error is handled properly.
Common Types of JavaScript Errors
Here are some common runtime errors developers encounter:
| Error Type | Meaning |
|---|---|
ReferenceError |
Variable does not exist |
TypeError |
Invalid operation on a value |
SyntaxError |
Invalid JavaScript syntax |
RangeError |
Value out of allowed range |
Error |
Generic error object |
Example of a TypeError:
const num = 10;
num.toUpperCase();
Output:
TypeError: num.toUpperCase is not a function
Numbers do not have the toUpperCase() method, so JavaScript throws an error.
Why Error Handling Matters
Without proper error handling:
Applications may crash
Users may see blank screens
Bugs become harder to identify
Important operations may fail silently
Good error handling helps developers:
Detect problems quickly
Prevent application crashes
Show meaningful messages to users
Debug issues efficiently
Build stable applications
Instead of letting the program fail abruptly, error handling allows applications to fail gracefully.
The Problem Without Error Handling
Consider this code:
console.log("Program started");
const user = JSON.parse("invalid json");
console.log("Program ended");
Output:
Program started
SyntaxError: Unexpected token i in JSON
The last line never runs because the error stops execution.
This can become dangerous in larger applications.
Using try and catch Blocks
JavaScript provides try and catch blocks to handle errors safely.
Basic syntax:
try {
// Code that may cause an error
} catch (error) {
// Code to handle the error
}
How try Works
The try block contains code that might fail.
Example:
try {
const result = JSON.parse("invalid json");
console.log(result);
}
If an error occurs, JavaScript immediately stops executing the try block and moves to catch.
How catch Works
The catch block receives the error object.
Example:
try {
const result = JSON.parse("invalid json");
} catch (error) {
console.log("Something went wrong");
console.log(error.message);
}
Output:
Something went wrong
Unexpected token i in JSON
Instead of crashing the application, the error is handled gracefully.
Understanding the Error Object
The catch block receives an error object containing useful debugging information.
Example:
try {
let data = JSON.parse("wrong");
} catch (error) {
console.log(error.name);
console.log(error.message);
}
Output:
SyntaxError
Unexpected token w in JSON
Useful properties:
| Property | Description |
|---|---|
error.name |
Type of error |
error.message |
Error message |
error.stack |
Stack trace for debugging |
Real-World Example of try and catch
Suppose a user enters JSON data into a form.
const userInput = '{"name":"Aslam"}';
try {
const user = JSON.parse(userInput);
console.log(user.name);
} catch (error) {
console.log("Invalid JSON format");
}
If the input is valid, the program works normally.
If the input is invalid, the application handles the error instead of crashing.
This creates a better user experience.
The finally Block
The finally block runs no matter what happens.
It executes:
Whether an error occurs or not
Whether the
tryblock succeeds or fails
Syntax:
try {
// risky code
} catch (error) {
// handle error
} finally {
// always runs
}
Example of finally
try {
console.log("Connecting to database");
} catch (error) {
console.log("Connection failed");
} finally {
console.log("Closing connection");
}
Output:
Connecting to database
Closing connection
The finally block is useful for cleanup tasks.
Common Uses of finally
Developers commonly use finally for:
Closing database connections
Stopping loaders/spinners
Releasing resources
Cleaning temporary files
Logging completion status
Example:
try {
console.log("File opened");
} catch (error) {
console.log("Error occurred");
} finally {
console.log("File closed");
}
Even if an error occurs, the file still gets closed properly.
Throwing Custom Errors
JavaScript also allows developers to create their own errors using throw.
Syntax:
throw new Error("Message");
Why Throw Custom Errors?
Custom errors help when:
Validating user input
Enforcing business rules
Detecting invalid states
Creating meaningful debugging messages
Example of throw
const age = 15;
try {
if (age < 18) {
throw new Error("User must be at least 18 years old");
}
console.log("Access granted");
} catch (error) {
console.log(error.message);
}
Output:
User must be at least 18 years old
This gives developers full control over error handling.
Throwing Different Types of Errors
You can throw different error types as well.
Example:
throw new TypeError("Invalid data type");
Or:
throw new RangeError("Value out of range");
Using specific error types improves debugging and code clarity.
Graceful Failure in Applications
Good applications do not completely break when something fails.
Instead, they:
Show error messages
Retry operations
Log issues for developers
Continue functioning where possible
For example:
try {
const response = fetchData();
displayData(response);
} catch (error) {
showErrorMessage("Unable to load data");
}
This approach protects the user experience.
Nested try and catch Blocks
JavaScript also allows nested error handling.
Example:
try {
try {
JSON.parse("wrong");
} catch (error) {
console.log("Inner error handled");
}
} catch (error) {
console.log("Outer error handled");
}
Nested handling is useful in large applications but should be used carefully to avoid complexity.
Best Practices for Error Handling
1. Handle Errors Properly
Do not ignore errors silently.
Bad:
catch (error) {
}
Good:
catch (error) {
console.log(error.message);
}
2. Use Meaningful Error Messages
Clear messages make debugging easier.
Bad:
throw new Error("Wrong");
Good:
throw new Error("Email format is invalid");
3. Avoid Excessive try Blocks
Only wrap code that may fail.
Bad:
try {
// huge application logic
}
Good:
try {
JSON.parse(data);
}
4. Log Errors During Development
Logging helps developers identify problems quickly.
Example:
catch (error) {
console.error(error);
}
5. Do Not Expose Sensitive Errors to Users
Developers need detailed errors.
Users need simple messages.
Bad:
DatabaseError: Connection failed at port 3306
Better:
Something went wrong. Please try again later.
Error Handling in Asynchronous JavaScript
Errors also happen in asynchronous code.
Example using async/await:
async function getData() {
try {
const response = await fetch("https://api.example.com/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.log("Failed to fetch data");
}
}
This is extremely common in modern JavaScript applications.
Final Thoughts
Errors are a normal part of software development. What separates strong applications from unstable ones is how those errors are handled.
JavaScript provides powerful tools like:
trycatchfinallythrow
These features help developers:
Prevent crashes
Handle failures gracefully
Improve debugging
Build reliable applications
