# Error Handling

Every developer learns this eventually, usually at the worst possible time. You write something, it works on your machine, you ship it, and then a user does something you didn't predict, types letters into a number field, loses their internet connection mid-request, passes in an empty string where you expected an array. The code falls over.

The mistake isn't that the code broke. That's going to happen. The mistake is not planning for it. A user who hits an unhandled error sees nothing useful. You, the developer, also see nothing useful, no message, no context, no idea what went wrong. Compare that to code that catches the error, logs it properly, and shows the user something sensible. Same breakage, completely different experience for everyone involved.

Before getting into how to handle errors, it's worth knowing the types you'll actually run into. There are three that show up constantly.

**ReferenceError:** You used a variable that doesn't exist. Usually a typo or a scoping problem.

**TypeError:** You tried to call something that isn't a function, or access a property on `null` or `undefined`.

**SyntaxError:** The code itself is malformed. A missing bracket, a bad JSON string. Usually caught before the script even runs.

**RangeError:** A value is outside an acceptable range.

**NetworkError:** Not a built-in JS type, but the most common runtime failure. The fetch failed. The API timed out. The user went offline.

## **Try and Catch**

The fix for unhandled errors is a `try...catch` block. Put the code that might fail inside `try`. If it throws, JavaScript jumps straight to `catch` and hands you the error object. The rest of `try` doesn't run and nothing crashes.

```javascript
try {
  // This will throw a ReferenceError
  const result = undeclaredVariable * 2;
  console.log(result); // never runs
} catch (err) {
  // err is the Error object JavaScript created
  console.log(err.name);    // "ReferenceError"
  console.log(err.message); // "undeclaredVariable is not defined"
}

// Code runs normally
console.log("we're still going");
```

The error object always has two properties you'll use: `name` tells you the type of error, and `message` tells you what went wrong. In modern environments there's also `stack`, which gives you the full call trace. That one is invaluable when you're debugging something three functions deep.

One thing that trips people up: `try...catch` only works on runtime errors. If your code has a syntax error, JavaScript never runs the block at all. And if you're working with async code, a plain `try...catch` won't catch promise rejections unless you're inside an `async` function. There's a separate section on that below.

![](https://cdn.hashnode.com/uploads/covers/678b775e773554ab7117f20a/b943c6cc-f0b9-4d8b-b84c-c9368d4c140e.png align="center")

## **The Finally Block**

There's a third part to the pattern that a lot of people skip until they actually need it. `finally` runs no matter what. Whether the `try` succeeded, whether `catch` ran, whether someone threw again inside `catch`. It doesn't matter. `finally` runs.

The use case is cleanup. If you opened a database connection, you need to close it. If you set a loading spinner to visible, you need to hide it. These things should happen regardless of whether the operation worked. That's exactly what `finally` is for.

```javascript
async function loadUserData(id) {
  showSpinner(); // loading starts

  try {
    const res  = await fetch(`/api/users/${id}`);
    const data = await res.json();
    renderProfile(data);
  } catch (err) {
    showErrorMessage("Couldn't load profile. Try again.");
    console.error(err);
  } finally {
    hideSpinner(); // always runs 
  }
}
```

Without `finally`, you'd have to call `hideSpinner()` in both the success path and the `catch` block. That's easy to forget, especially when the function grows. `finally` means you write that cleanup logic once and it always runs.

![](https://cdn.hashnode.com/uploads/covers/678b775e773554ab7117f20a/8cc91f93-151f-41e0-a96a-2fecb4789658.png align="center")

A good mental model: `try` is the attempt, `catch` is the fallback, `finally` is the cleanup.

## **Throwing Custom Errors**

JavaScript lets you throw errors yourself with the `throw` keyword. You can technically throw anything, a string, a number, an object. In practice, always throw an `Error` object. That's because `Error` objects include a stack trace automatically, which is the thing you actually want when debugging.

```javascript
function divide(a, b) {
  if (b === 0) {
    throw new Error("Division by zero is not allowed");
  }
  return a / b;
}

try {
  divide(10, 0);
} catch (err) {
  console.log(err.message); // "Division by zero is not allowed"
}
```

For bigger applications, you'll want to extend `Error` with custom classes. This way a `catch` block can check what kind of error it got and respond differently, show a 404 UI for not-found errors, show a login prompt for auth errors, and so on.

```javascript
class NotFoundError extends Error { 
    constructor(resource) { 
        super(${resource} not found); 
        this.name = "NotFoundError"; 
        this.resource = resource; 
      } 
}
class AuthError extends Error { 
    constructor(msg) { 
        super(msg); 
        this.name = "AuthError";
    }
}
async function getPost(id) { 
    const res = await fetch(/api/posts/${id});

    if (res.status === 401) throw new AuthError("Session expired"); 
    if (res.status === 404) throw new NotFoundError("post");
    return res.json(); 
}

try { 
    await getPost(99); 
} catch (err) { 
    if (err instanceof AuthError) redirectToLogin(); 
    if (err instanceof NotFoundError) show404Page(); 
    else showGenericError(err.message); 
}
```

The `instanceof` check is what makes this pattern useful. One `catch` block, multiple kinds of failure, each handled differently. And if something unexpected comes through, the `else` branch catches it so nothing goes silently missing.

## **Why This Matters**

![](https://cdn.hashnode.com/uploads/covers/678b775e773554ab7117f20a/2490cecc-5c50-4918-a358-916081994389.png align="center")

*   [MDN Web Docs : try...catch](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/try...catch)
    
*   [MDN Web Docs : Error object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)
    

*   [javascript.info : Error handling, "try...catch"](https://javascript.info/try-catch)
    
*   [javascript.info : Custom errors, extending Error](https://javascript.info/custom-errors)
