# Exceptions and Errors — Dart

Source: https://www.skillbyai.com/en/dart/fn-errors

> Throw and handle exceptions, and distinguish Exception from Error.

## Failing clearly

Dart can throw any non-null object, but by convention you throw objects implementing **`Exception`** for conditions callers may reasonably handle (a `FormatException` from parsing, a custom `PaymentDeclinedException`) and subclasses of **`Error`** for **programming bugs** that should not be caught in normal code (`ArgumentError`, `StateError`, `RangeError`, `TypeError`, `UnimplementedError`). Handle exceptions with `try` followed by **`on Type`** clauses (match by type), **`catch (e, stackTrace)`** to access the object and stack trace, and **`finally`** for cleanup. **`rethrow`** rethrows the current exception while preserving the stack trace. Dart has no checked exceptions, so document what a function may throw. Validate arguments early with `ArgumentError.checkNotNull` or by throwing `ArgumentError.value(...)`. In asynchronous code, errors travel through Futures and Streams and are caught with `try`/`catch` around `await`. Some codebases prefer returning **result types** (a sealed class with success and failure subclasses) for expected failures, combining nicely with Dart 3 pattern matching.

## Custom exceptions, on clauses and rethrow

Specific handlers first; programming errors are not swallowed.

```dart
class PaymentDeclinedException implements Exception {
  PaymentDeclinedException(this.reason);
  final String reason;
  @override
  String toString() => 'PaymentDeclinedException: $reason';
}

String charge(double amount, String token) {
  if (amount <= 0) throw ArgumentError.value(amount, 'amount', 'must be positive');   // a bug in the caller
  if (token.startsWith('blocked')) throw PaymentDeclinedException('Card blocked by issuer');
  return 'pay_${DateTime.now().millisecondsSinceEpoch}';
}

void main() {
  try {
    final id = charge(499, 'blocked_tok');
    print('Paid $id');
  } on PaymentDeclinedException catch (e) {
    print('Declined: ${e.reason}');
  } on FormatException {
    print('Bad input');
  } catch (e, stackTrace) {
    print('Unexpected: $e');
    rethrow;                                   // keep the original stack trace
  } finally {
    print('Payment attempt finished');
  }
}
```

## Do not catch Error subclasses routinely

Catching `Error` (such as `RangeError` or `StateError`) hides bugs that should be fixed. Catch specific `Exception` types you can handle and let programming errors reach a top-level handler and crash reports.

**Quiz:** Which keyword rethrows the current exception while preserving its stack trace?

- [ ] throw
- [ ] finally
- [x] rethrow
- [ ] on

*Answer:* rethrow. rethrow keeps the original stack trace, unlike throw e.
