Error Handling in JavaScript: try/catch, throw, and Advanced Techniques

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Error Handling is the Foundation of Reliable Code

Any application running on Node.js will sooner or later encounter exceptional situations: invalid user input, network failure, missing file on disk, or incorrect data from the database. Without proper error handling, these situations lead to server crashes, memory leaks, or undefined behavior. In JavaScript, especially in the Node.js environment, the try/catch mechanism is a fundamental tool for building fault-tolerant systems. In this article, we will cover not only the classic try/catch but also advanced techniques: custom error classes, working with promises and async functions, as well as best practices for logging.



Basics: try, catch, and finally blocks

The basic syntax for error handling in JavaScript looks like this:

try {  // Code that may throw an exception  const data = JSON.parse('{"name": "Node.js"}');  console.log(data.name);} catch (error) {  // Error handling  console.error('An error occurred:', error.message);} finally {  // Code that will execute in any case  console.log('Finally block executed');}

How it works:

  • try — contains potentially dangerous code.
  • catch — catches the exception if it occurs. The error parameter receives the error object.
  • finally — always executes, regardless of whether there was an error or not. Ideal for closing files, releasing resources, or completing transactions.

It is important to remember: finally will execute even after a return in the try or catch block.



Types of Errors in JavaScript

Built-in error constructors help classify exceptions more precisely:

  • Error — the base class for all errors.
  • SyntaxError — a syntax error (e.g., invalid JSON).
  • TypeError — an incorrect data type (e.g., calling a non-function).
  • ReferenceError — referencing a non-existent variable.
  • RangeError — exceeding allowable limits (e.g., recursion).

Example of handling different types:

try {  const obj = null;  console.log(obj.property); // TypeError} catch (err) {  if (err instanceof TypeError) {    console.log('Type error:', err.message);  } else if (err instanceof ReferenceError) {    console.log('Variable not found');  } else {    console.log('Unknown error:', err);  }}


Generating Custom Errors: the throw Operator

Sometimes it is necessary to explicitly indicate that an exceptional situation has occurred in the program. The throw operator is used for this. It can throw anything: a string, a number, an object, but it is best to throw an instance of the Error class or its descendant.

function divide(a, b) {  if (b === 0) {    throw new Error('Division by zero is not allowed');  }  if (typeof a !== 'number' || typeof b !== 'number') {    throw new TypeError('Arguments must be numbers');  }  return a / b;}

try { const result = divide(10, 0); console.log(result);} catch (err) { console.error(err.name + ': ' + err.message);}

Why is it better to throw an Error rather than a string? The Error object contains a call stack (stack), which is critically important for debugging in Node.js. A string will not provide such information.



Creating Custom Error Classes

In real projects (e.g., an Express web server), it is convenient to have custom error types for different situations: ValidationError, NotFoundError, DatabaseError. This simplifies centralized handling.

class ValidationError extends Error {                

Blogs

Book Recommendations