# Error Handling in JavaScript: Try, Catch, Finally

Modern JavaScript applications are expected to be **robust, fault-tolerant, and user-friendly**. But in reality, things go wrong—APIs fail, user input is invalid, or unexpected runtime conditions occur.

This is where **error handling** becomes essential.

* * *

## 🚨 A Simple Runtime Error Example

Let’s start with something basic:

```js
let user = null;
console.log(user.name); // ❌ TypeError
```

This throws:

```plaintext
TypeError: Cannot read properties of null
```

If not handled, this can **crash your app** or break user experience.

* * *

## ❓ What Are Errors in JavaScript?

In JavaScript, errors are objects that represent **unexpected or invalid operations** during execution.

### Common Types of Errors:

*   **SyntaxError** → Invalid code syntax
    
*   **ReferenceError** → Using undeclared variables
    
*   **TypeError** → Invalid operations on values
    
*   **RangeError** → Values out of allowed range
    

Example:

```js
console.log(x); // ReferenceError: x is not defined
```

* * *

## 🧠 Why Error Handling Matters

Without proper error handling:

*   Your app may **crash abruptly**
    
*   Users get **poor experience**
    
*   Debugging becomes **difficult**
    
*   Systems become **unreliable**
    

With proper handling:

*   You can **fail gracefully**
    
*   Show meaningful messages
    
*   Log errors for debugging
    
*   Maintain application stability
    

* * *

## 🛠️ Using `try...catch`

The `try...catch` block allows you to **catch runtime errors** and handle them safely.

### Basic Syntax:

```js
try {
  // Code that may throw error
} catch (error) {
  // Handle error
}
```

### Example:

```js
try {
  let user = null;
  console.log(user.name);
} catch (error) {
  console.log("Something went wrong:", error.message);
}
```

👉 Output:

```plaintext
Something went wrong: Cannot read properties of null
```

* * *

## 🔍 How It Works Internally

*   Code inside `try` runs normally
    
*   If an error occurs → execution jumps to `catch`
    
*   Remaining `try` code is skipped
    
*   `catch` receives the error object
    

* * *

## 🧩 The `finally` Block

The `finally` block runs **no matter what happens**.

### Syntax:

```js
try {
  // risky code
} catch (error) {
  // handle error
} finally {
  // always runs
}
```

### Example:

```js
try {
  console.log("Trying...");
  throw new Error("Oops!");
} catch (error) {
  console.log("Caught:", error.message);
} finally {
  console.log("Cleanup done.");
}
```

👉 Output:

```plaintext
Trying...
Caught: Oops!
Cleanup done.
```

### Use Cases:

*   Closing resources (files, DB connections)
    
*   Cleanup tasks
    
*   Logging
    

* * *

## 🎯 Throwing Custom Errors

JavaScript allows you to **create and throw your own errors**.

### Basic Example:

```js
throw new Error("Something went wrong!");
```

### Custom Validation Example:

```js
function withdraw(amount) {
  if (amount <= 0) {
    throw new Error("Amount must be greater than zero");
  }
  console.log("Withdrawal successful");
}

try {
  withdraw(-100);
} catch (error) {
  console.log(error.message);
}
```

👉 Output:

```plaintext
Amount must be greater than zero
```

* * *

## 🧱 Custom Error Types (Advanced)

You can create custom error classes:

```js
class ValidationError extends Error {
  constructor(message) {
    super(message);
    this.name = "ValidationError";
  }
}

throw new ValidationError("Invalid input");
```

* * *

## ⚠️ Important Notes

### 1\. `try...catch` only works for runtime errors

It does NOT catch:

*   Syntax errors (before execution)
    
*   Errors outside its block
    

* * *

### 2\. Async Code Needs Special Handling

```js
try {
  fetch("invalid-url"); // ❌ won't be caught
} catch (err) {
  console.log(err);
}
```

Use `async/await`:

```js
try {
  const res = await fetch("invalid-url");
} catch (err) {
  console.log("Caught async error:", err);
}
```

* * *

## 💡 Graceful Failure (Key Concept)

Instead of crashing:

❌ Bad:

```js
console.log(user.name);
```

✅ Good:

```js
if (user) {
  console.log(user.name);
} else {
  console.log("User not found");
}
```

👉 Always aim to:

*   Prevent crashes
    
*   Provide fallback behavior
    
*   Inform users properly
    

* * *

## 🐞 Debugging Benefits

Error handling helps you:

*   Capture error messages (`error.message`)
    
*   Track stack traces (`error.stack`)
    
*   Log issues for production debugging
    

Example:

```js
catch (error) {
  console.error(error.stack);
}
```
