# Exception Handling

Course: [Dart](https://www.devtutorials.ai/learn/dart) · Section: Exception Handling
Source: https://www.devtutorials.ai/learn/dart/exceptions/exception-handling

> How to catch errors with try, on, catch and finally, throw and rethrow them, write your own exceptions, and read Dart's built-in errors.

## What Is an Exception?

**Exception** = a problem that happens **while the program runs**, and stops the normal flow.

- A user types `abc` where a number was expected.
- A file the program needs is missing.
- A list is empty, but the code asks for its first item.
- If nobody **handles** the problem, the program **crashes** and prints `Unhandled exception`.

**Simple lesson:** an exception is a "something went wrong" signal, and you can catch it so the program keeps going.

### A Real-World Example

Think of a school bus trip.

- The plan: go to the museum, see the exhibits, come back.
- Problem: a tyre goes flat on the way. That is the **exception**.
- The driver does not give up the whole trip. He has a plan: change the tyre, or call another bus. That is **handling**.
- And whatever happens, everyone does a **head count** at the end. That is `finally`.

Choose what a student typed, and watch which blocks run:

Every **Run** box on this page runs on the course's Dart runner in your browser.

Some boxes fail **on purpose**, to show the error Dart prints. They are marked as expected errors.

## What You Will Learn

In this lesson, you will learn:

1. The kinds of mistakes in a program, and how errors differ from exceptions.
2. How `try`, `on`, `catch` and `finally` work, and in what order handlers are tried.
3. How to read a stack trace.
4. How to `throw`, `rethrow`, and make your own exceptions.
5. The common built-in errors and exceptions.

## Kinds of Problems

### Syntax, Runtime and Logic Errors

| Kind | When it is found | Example | Who catches it |
|---|---|---|---|
| **Syntax / compile error** | before running | a missing `;` | Dart, before the program starts |
| **Runtime error** | while running | `int.parse('abc')` | your `try` and `catch` |
| **Logic error** | never, by Dart | `total = a - b` when you meant `+` | only testing |

`try` and `catch` handle **runtime** problems. They cannot fix wrong logic or wrong syntax.

```dart runnable expect-error title="An unhandled exception crashes the program"
void main() {
  print('Reading marks...');
  var marks = int.parse('ninety');
  print('Marks: $marks');
}
```

The last line never runs. The program stopped at `int.parse`.

### Errors vs Exceptions

| | `Exception` | `Error` |
|---|---|---|
| Means | an expected problem from outside | a **bug** in the code |
| Example | bad user input, missing file | index out of range, `null` where not allowed |
| Should you catch it? | **yes**, and recover | usually **no**: fix the code |
| Built-in examples | `FormatException`, `TimeoutException` | `RangeError`, `StateError`, `TypeError` |

**Simple lesson:** catch exceptions to recover, and fix the code that causes errors.

## try, on, catch and finally

### The Basic Shape

```text
try {
  // code that might fail
} on SomeException {
  // runs for that type
} catch (e) {
  // runs for anything else, e is the problem
} finally {
  // runs every time
}
```

```dart runnable title="Reading marks safely"
void main() {
  for (var text in ['85', 'abc']) {
    try {
      var marks = int.parse(text);
      print('Marks: $marks');
    } on FormatException {
      print('"$text" is not a number');
    } finally {
      print('Checked "$text"');
    }
  }
  print('The program is still running.');
}
```

### on, catch and Both

- **`on Type`**: catch only this type. You do not get the object.
- **`catch (e)`**: catch anything. `e` is the thrown object.
- **`on Type catch (e)`**: catch only this type, **and** get the object.

```dart runnable title="on Type catch (e) gives you the details"
void main() {
  try {
    int.parse('12a');
  } on FormatException catch (e) {
    print('Problem: ${e.message}');
    print('Type: ${e.runtimeType}');
  }
}
```

### Matching Order

Handlers are tried **from top to bottom**. The **first** one that fits runs. The rest are skipped.

Put **specific** types first and a general `catch (e)` **last**.

If `catch (e)` comes first, it catches everything, and the specific handlers below it never run.

### finally

**`finally`** = a block that runs **every time**: after success, after a caught error, and even when the error is not caught.

- Use it for clean-up: close a file, stop a timer, print "done".

**Simple lesson:** `try` the risky code, `on`/`catch` the problems you expect, and clean up in `finally`.

## Stack Traces

**Stack trace** = the list of function calls that were running when the problem happened. It shows **where** it went wrong.

- Get it as the **second** name in `catch`: `catch (e, s)`.
- Read it from the **top**: the top line is where the error was thrown.

```dart
void main() {
  try {
    loadMarks();
  } catch (e, s) {
    print(e);
    print(s);
  }
}
// FormatException: Invalid radix-10 number (at character 1)
// #0      int.parse (dart:core-patch/integers_patch.dart:52:7)
// #1      loadMarks (file:///school/bin/main.dart:12:3)
// #2      main (file:///school/bin/main.dart:3:5)
```

- Line `#1` says the problem came from `loadMarks`, line 12.
- The course runner prints a short stack trace. Your computer prints the full one.

**Simple lesson:** a stack trace is a trail of footprints that leads you to the line that failed.

## throw and rethrow

### Throwing Your Own Problem

**`throw`** = signal a problem yourself. Usually you throw an `Exception` or an `Error` object.

```dart runnable title="Throwing when the input is wrong"
int checkMarks(int marks) {
  if (marks < 0 || marks > 100) {
    throw ArgumentError('Marks must be 0 to 100, not $marks');
  }
  return marks;
}

void main() {
  try {
    print(checkMarks(88));
    print(checkMarks(140));
  } on ArgumentError catch (e) {
    print(e.message);
  }
}
```

### Propagation

**Propagation** = an uncaught exception **rises up** out of each function, to the one that called it, until a `catch` stops it.

```dart runnable title="The problem rises from parse to main"
int parse(String text) => int.parse(text);

int loadMarks(String text) {
  print('loadMarks starts');
  var m = parse(text);
  print('loadMarks ends');
  return m;
}

void main() {
  try {
    loadMarks('x9');
  } on FormatException {
    print('main caught it');
  }
}
```

`loadMarks ends` never prints. The exception jumped straight out of `loadMarks`.

### rethrow

**`rethrow`** = inside a `catch`, pass the **same** problem on to the caller. Useful when you want to note it, but not handle it here.

```dart runnable title="Note it, then pass it on"
void saveMarks(String text) {
  try {
    int.parse(text);
  } catch (e) {
    print('saveMarks: could not save "$text"');
    rethrow;
  }
}

void main() {
  try {
    saveMarks('ten');
  } on FormatException catch (e) {
    print('main: ${e.message}');
  }
}
```

**Simple lesson:** `throw` starts a problem, it rises until caught, and `rethrow` sends a caught one further up.

## Custom Exceptions

**Custom exception** = your own exception class. Make it `implements Exception`.

```dart runnable title="A fee exception with details"
class FeeNotPaidException implements Exception {
  final String student;
  final int due;
  FeeNotPaidException(this.student, this.due);

  @override
  String toString() => 'FeeNotPaidException: $student owes Rs. $due';
}

void issueHallTicket(String name, int due) {
  if (due > 0) {
    throw FeeNotPaidException(name, due);
  }
  print('Hall ticket issued to $name');
}

void main() {
  for (var (name, due) in [('Asha', 0), ('Ravi', 1500)]) {
    try {
      issueHallTicket(name, due);
    } on FeeNotPaidException catch (e) {
      print(e);
      print('Please pay Rs. ${e.due} at the office.');
    }
  }
}
```

- A custom type lets callers catch **exactly** this problem with `on FeeNotPaidException`.
- Fields like `due` carry the details.
- Override `toString()` so the message is clear.

### Nested Handlers

A `try` can sit **inside** another. The inner one gets the first chance:

```dart runnable title="Inner handler first, outer handler for the rest"
void main() {
  try {
    try {
      var data = [1, 2];
      print(data[9]);
    } on FormatException {
      print('inner: format problem');
    } finally {
      print('inner finally');
    }
  } on RangeError {
    print('outer: position problem');
  }
}
```

The inner `try` had no handler for `RangeError`, so it rose to the outer one. The inner `finally` still ran first.

**Simple lesson:** make your own exception classes so callers can catch exactly the problem they understand.

## Built-in Errors and Exceptions

| Name | Thrown when | Example |
|---|---|---|
| `FormatException` | text is in the wrong format | `int.parse('abc')` |
| `ArgumentError` | a function gets a bad argument | your own `throw ArgumentError(...)` |
| `RangeError` | an index or value is out of range | `[1, 2][5]` |
| `StateError` | the object is in the wrong state | `[].first` |
| `UnsupportedError` | the operation is not allowed | adding to a `const` list |
| `UnimplementedError` | a method is not written yet | `throw UnimplementedError();` |
| `TypeError` | a value has the wrong type, or `!` on `null` | `String? s; s!.length` |
| `NoSuchMethodError` | a missing method is called on a `dynamic` value | `dynamic d = 5; d.fly();` |
| `AssertionError` | an `assert` fails (when asserts are on) | `assert(age > 0)` |
| `ConcurrentModificationError` | a list changes while a loop reads it | `for (x in l) l.add(x)` |

```dart runnable title="Meeting a few built-in errors"
void main() {
  try {
    var empty = <int>[];
    print(empty.first);
  } on StateError catch (e) {
    print('StateError: ${e.message}');
  }

  try {
    const houses = ['Red', 'Blue'];
    houses.add('Pink');
  } on UnsupportedError catch (e) {
    print('UnsupportedError: ${e.message}');
  }

  try {
    var marks = [80, 90];
    for (var m in marks) {
      marks.add(m + 1);
    }
  } on ConcurrentModificationError {
    print('ConcurrentModificationError: do not change a list while looping over it');
  }
}
```

**Easy way to remember**

- **try**: "let me try this".
- **on Type**: "if it is this problem...".
- **catch (e, s)**: "...give me the problem and where it happened".
- **finally**: "no matter what, do this".
- **throw / rethrow**: "raise a problem / pass it on".

**Simple lesson:** the built-in names tell you what went wrong, so read the first word of every error.

## TL;DR

1. An **exception** is a runtime problem. Unhandled, it crashes the program.
2. **`try`** risky code. **`on Type`**, **`catch (e, s)`** handle it. **`finally`** always runs.
3. Handlers are tried **top to bottom**. Specific first, general `catch` last.
4. **`throw`** raises a problem. It **propagates** up until caught. **`rethrow`** passes it on.
5. **Custom exceptions:** `class X implements Exception`. Catch `Exception`s, fix `Error`s.

## Common mistakes

| Mistake | Wrong idea | Correct idea | What goes wrong |
|---|---|---|---|
| `catch (e)` before `on FormatException` | Order does not matter | The first match wins | The specific handler never runs. |
| An empty `catch (e) {}` | Hiding errors is safe | At least print or log it | Bugs disappear silently. |
| Catching `Error`s to hide bugs | All problems should be caught | Fix the code that causes errors | The bug stays in the program. |
| `throw e;` inside catch | Same as `rethrow` | `rethrow` keeps the original stack trace | The trail to the real line is lost. |
| A custom exception that extends nothing | Any class can be thrown | `implements Exception` makes intent clear | `on Exception` will not catch it. |
| Expecting code after a throw to run | Dart finishes the block | Control jumps to the handler | Output you expected never appears. |

## Check Yourself

## Exercises

## What to Remember

- An exception is a runtime problem. Unhandled, it stops the program.
- `try` the risky code, handle it with `on` and `catch`, and clean up in `finally`.
- Handlers are tried top to bottom, so put specific types first.
- `throw` raises a problem, it rises until caught, and `rethrow` passes it on.
- Make custom exceptions with `implements Exception`, and fix the code behind `Error`s.

Next: **Async Basics and Futures**. You will run slow work without freezing your program.
