# Error Handling in JavaScript: Try, Catch, Finally


# Error Handling in JavaScript: Try, Catch, Finally

Code breaks. Users do unexpected things. Networks fail. Without error handling, your app crashes. With it, you handle problems gracefully and stay in control.

Error handling isn't about preventing all mistakes. It's about deciding what to do when they happen.

* * *

## What Errors Are in JavaScript

An error is when something goes wrong while code runs. JavaScript throws an error and stops.

### Common Error Types

**Reference Error — Using a variable that doesn't exist:**

```javascript
console.log(someVariable);
// ReferenceError: someVariable is not defined
```

**Type Error — Calling a method on the wrong type:**

```javascript
let number = 42;
number.toUpperCase();
// TypeError: number.toUpperCase is not a function
```

**Range Error — Value outside acceptable range:**

```javascript
new Array(-1);
// RangeError: Invalid array length
```

### Without Error Handling

```javascript
function loadFile(filename) {
  const data = JSON.parse(fs.readFileSync(filename, "utf8"));
  return data;
}

loadFile("missing.json");
// Error: file not found
// App crashes. User sees nothing.
```

### With Error Handling

```javascript
function loadFile(filename) {
  try {
    const data = JSON.parse(fs.readFileSync(filename, "utf8"));
    return data;
  } catch (error) {
    return { default: true };
  }
}

loadFile("missing.json");
// Returns: { default: true }
// App keeps running.
```

* * *

## Using Try and Catch Blocks

The `try` block has code that might fail. The `catch` block handles it if it does.

### Basic Structure

```javascript
try {
  // Code that might throw an error
  riskyOperation();
} catch (error) {
  // Handle the error
  console.log("Something went wrong:", error.message);
}
```

### Real Example: Parsing JSON

```javascript
let jsonString = '{ "name": "Alice" }';

try {
  let data = JSON.parse(jsonString);
  console.log("Parsed successfully:", data);
} catch (error) {
  console.log("Invalid JSON:", error.message);
}
```

If it's valid JSON, it parses. If not, `catch` handles it.

### With Broken JSON

```javascript
let jsonString = '{ "name": "Alice"';  // Missing closing brace

try {
  let data = JSON.parse(jsonString);
  console.log("Parsed:", data);
} catch (error) {
  console.log("Invalid JSON:", error.message);
  // Output: Invalid JSON: Unexpected end of JSON input
}
```

The error happens in `try`. Instead of crashing, `catch` handles it.

### The Error Object

The error object has useful properties:

```javascript
try {
  nonexistentFunction();
} catch (error) {
  console.log(error.message);  // The message
  console.log(error.name);     // Error type
  console.log(error.stack);    // Full trace
}
```

### Multiple Operations

```javascript
try {
  const data = fs.readFileSync("data.json", "utf8");
  const parsed = JSON.parse(data);
  processData(parsed);
} catch (error) {
  console.log("Error:", error.message);
}
```

If any operation fails, `catch` handles it.

* * *

## The Finally Block

The `finally` block always runs, no matter what. Success or failure, it executes.

### Structure

```javascript
try {
  // Code that might fail
} catch (error) {
  // Handle the error
} finally {
  // Always runs
}
```

### Real Example: Close a Database

```javascript
const db = openDatabase("app.db");

try {
  const user = db.query("SELECT * FROM users WHERE id = 1");
  console.log("User found:", user);
} catch (error) {
  console.log("Query failed:", error.message);
} finally {
  db.close();  // Always close, success or failure
}
```

The database closes whether the query works or fails.

### Execution Order

```plaintext
try block runs
  |
  ├─ Success → skip catch → go to finally
  └─ Failure → catch runs → then finally

finally always runs last
```

### Resource Cleanup

```javascript
function processFile(filename) {
  let file;
  
  try {
    file = openFile(filename);
    const content = file.read();
    console.log("File processed");
  } catch (error) {
    console.log("Error:", error.message);
  } finally {
    if (file) {
      file.close();  // Clean up
    }
  }
}
```

The file closes whether processing succeeds or fails.

* * *

## Try → Catch → Finally Execution Flow

```plaintext
START
  |
  v
TRY BLOCK
  |
  ├─── No error
  |      |
  |      v
  |    Code runs successfully
  |      |
  |      v
  |    SKIP catch block
  |      |
  └──────┤
  |      |
  ├─── Error occurs
  |      |
  |      v
  |    Jump to CATCH block
  |      |
  |      v
  |    Handle error
  |      |
  └──────┤
  |      |
  v      v
FINALLY BLOCK (always runs)
  |
  v
END
```

* * *

## Throwing Custom Errors

When something is wrong with your logic, create your own error.

### Throw Statement

```javascript
function validateAge(age) {
  if (age < 0) {
    throw new Error("Age cannot be negative");
  }
  return age;
}

try {
  validateAge(-5);
} catch (error) {
  console.log("Validation failed:", error.message);
  // Output: Validation failed: Age cannot be negative
}
```

### Error Types You Can Throw

```javascript
throw new Error("Something went wrong");
throw new TypeError("Expected a number");
throw new RangeError("Value out of range");
throw "Quick error message";
```

### Custom Error Classes

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

function validateEmail(email) {
  if (!email.includes("@")) {
    throw new ValidationError("Invalid email");
  }
  return email;
}

try {
  validateEmail("notanemail");
} catch (error) {
  if (error instanceof ValidationError) {
    console.log("Email error:", error.message);
  }
}
```

### When to Throw

```javascript
function divideNumbers(a, b) {
  if (typeof a !== "number" || typeof b !== "number") {
    throw new TypeError("Both must be numbers");
  }
  
  if (b === 0) {
    throw new Error("Cannot divide by zero");
  }
  
  return a / b;
}

try {
  divideNumbers(10, 2);   // Works: 5
  divideNumbers(10, 0);   // Throws error
} catch (error) {
  console.log("Math error:", error.message);
}
```

* * *

## Why Error Handling Matters

### 1. Graceful Failure

Without error handling, your app crashes:

```javascript
// Crashes if database fails
app.get("/user/:id", (req, res) => {
  const user = db.findUser(req.params.id);
  res.json(user);
});

// Handles the error gracefully
app.get("/user/:id", (req, res) => {
  try {
    const user = db.findUser(req.params.id);
    res.json(user);
  } catch (error) {
    res.status(500).json({ error: "User not found" });
  }
});
```

User gets a message instead of a blank screen.

### 2. Better Debugging

Error objects show you where the problem is:

```javascript
try {
  riskyFunction();
} catch (error) {
  console.log(error.stack);
  // Shows file, line number, and function names
  // Helps you find bugs fast
}
```

### 3. Resource Cleanup

With `finally`, resources always get cleaned up:

```javascript
let connection;

try {
  connection = openConnection();
} catch (error) {
  console.log("Error:", error.message);
} finally {
  if (connection) {
    connection.close();  // Don't leak resources
  }
}
```

Without `finally`, connections stay open if an error happens.

### 4. Better User Experience

Handle errors cleanly:

```javascript
function saveData(data) {
  try {
    validateData(data);
    db.save(data);
    return { success: true };
  } catch (error) {
    return { 
      success: false, 
      message: "Could not save. Please try again." 
    };
  }
}
```

Users see a clear message, not a crash.

* * *

## Real-World Example: Parsing User Input

```javascript
function processUserInput(jsonString) {
  try {
    // Parse the JSON
    const data = JSON.parse(jsonString);
    
    // Validate the data
    if (!data.name || !data.email) {
      throw new Error("Missing required fields");
    }
    
    // Process the data
    const result = {
      name: data.name.trim(),
      email: data.email.toLowerCase()
    };
    
    console.log("Processing successful:", result);
    return result;
    
  } catch (error) {
    if (error instanceof SyntaxError) {
      console.log("Invalid JSON format:", error.message);
    } else if (error.message.includes("required")) {
      console.log("Validation error:", error.message);
    } else {
      console.log("Unexpected error:", error.message);
    }
    
    return null;
    
  } finally {
    console.log("Processing complete");
  }
}

// Test it
processUserInput('{ "name": "Alice", "email": "alice@example.com" }');
// Output: Processing successful: { name: 'Alice', email: 'alice@example.com' }
//         Processing complete

processUserInput('invalid json');
// Output: Invalid JSON format: Unexpected token i...
//         Processing complete

processUserInput('{ "name": "Bob" }');
// Output: Validation error: Missing required fields
//         Processing complete
```

Each scenario is handled appropriately.

* * *

## Error Handling Patterns

### Pattern 1: Try-Catch-Finally

```javascript
try {
  // Do something
} catch (error) {
  // Handle error
} finally {
  // Cleanup
}
```

### Pattern 2: Catch and Rethrow

```javascript
try {
  database.query("SELECT * FROM users");
} catch (error) {
  console.log("Database error:", error);
  throw error;  // Pass it up to the caller
}
```

### Pattern 3: Catch and Return Default

```javascript
function getConfig() {
  try {
    return loadConfig();
  } catch (error) {
    return defaultConfig;  // Use defaults if load fails
  }
}
```

### Pattern 4: Catch Specific Errors

```javascript
try {
  operation();
} catch (error) {
  if (error instanceof TypeError) {
    console.log("Type error:", error.message);
  } else if (error instanceof RangeError) {
    console.log("Range error:", error.message);
  } else {
    console.log("Unknown error:", error.message);
  }
}
```

* * *

## Practice Assignment

**1. Parse JSON safely:**

```javascript
const jsonString = '{ "name": "Alice", "age": 25 }';

// Write a function that parses the JSON
// If parsing fails, return a default object
```

**2. Handle multiple errors:**

```javascript
function divideAndLog(a, b) {
  try {
    // Check if both are numbers
    // Check if b is not zero
    // Divide and log result
  } catch (error) {
    // Handle the error
  }
}
```

**3. Create a custom error:**

```javascript
// Create a PasswordError class
// Throw it if password is too short
// Catch it and show a message
```

**4. Use finally for cleanup:**

```javascript
let file = null;

try {
  file = openFile("data.txt");
  // Read and process the file
} catch (error) {
  // Handle error
} finally {
  // Make sure file closes
}
```

* * *

## Quick Recap

* **Errors** happen when code breaks. JavaScript throws and stops.
  
* **Try block** contains code that might fail.
  
* **Catch block** handles errors if they happen.
  
* **Finally block** always runs, success or failure.
  
* **Throw** keyword creates your own errors.
  
* Error objects have `.message`, `.stack`, and `.name`.
  
* Error handling lets you fail gracefully instead of crashing.
  
* Use `finally` to clean up resources.
  
* Catch specific error types for different handling.
  
* Error messages help you debug faster.
  
* Good error handling improves user experience.

Error handling isn't optional. It's how you control what happens when things go wrong.

Happy coding! 🚀

---

> If you enjoyed this article, check out my other blogs on this profile.
> 🔗 Connect with me: [LinkedIn](https://linkedin.com/in/rajharsh03) | [GitHub](https://github.com/RajHarsh03) | [X (Twitter)](https://x.com/rajharsh03)
