100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
Programming

Exception Handling in Dart

Learn how Dart represents and handles runtime errors using try/catch/finally, custom exception types, and the distinction between Exceptions and Errors.

Async & GenericsBeginner7 min readJul 10, 2026
Analogies

Throwing and Catching Exceptions

Dart lets you throw any non-null object — not just subclasses of Exception or Error, though those are the conventions — and catch it with try { ... } catch (e) { ... } or, to only handle a specific type, on FormatException catch (e) { ... }. Multiple on clauses can be chained to handle different exception types differently, falling through to a general catch (e) as a last resort, and a bare throw; inside a catch block (with no expression) re-throws the currently caught exception, preserving its original stack trace for debugging.

🏏

Cricket analogy: Throwing an exception is like a batsman getting a genuine LBW dismissal — a specific, recognizable event — and Dart's on FormatException catch clauses are like an umpire's decision tree that handles LBW differently from a run-out, only falling back to a general ruling when the dismissal type doesn't match any specific rule.

dart
class InsufficientFundsException implements Exception {
  final String message;
  InsufficientFundsException(this.message);

  @override
  String toString() => 'InsufficientFundsException: $message';
}

void withdraw(double balance, double amount) {
  if (amount > balance) {
    throw InsufficientFundsException(
        'Cannot withdraw $amount from a balance of $balance');
  }
  print('Withdrew $amount successfully.');
}

void main() {
  try {
    withdraw(100.0, 250.0);
  } on InsufficientFundsException catch (e, stackTrace) {
    print('Caught: $e');
    // Log stackTrace to crash reporting here.
  } catch (e) {
    print('Unexpected error: $e');
  } finally {
    print('Transaction attempt complete.');
  }
}

Exception vs Error: Two Different Failure Categories

Dart's convention splits failures into two categories: classes implementing the Exception interface (like FormatException or a custom InsufficientFundsException) represent expected, often recoverable failure conditions worth catching and handling, while subclasses of Error (like RangeError, ArgumentError, StateError, or TypeError) signal programming bugs — an invalid index, a violated precondition, calling a method at the wrong time — that generally shouldn't be caught and 'handled', because the right fix is correcting the code that caused them, not papering over the symptom at runtime.

🏏

Cricket analogy: An Exception is like a rain delay — an expected, recoverable disruption the umpires have a standard protocol for — while an Error is like a groundskeeper discovering the pitch was never rolled before the match, a fundamental setup failure that should be fixed before play, not worked around mid-game.

When defining a custom exception, implement the Exception interface and include a descriptive message field, plus any structured data callers might need (an error code, the invalid value) — this makes the exception far more useful to catch and act on than a generic thrown String.

finally, Stack Traces, and rethrow

A finally block runs after the try/catch regardless of whether an exception was thrown, caught, or not thrown at all, making it the right place for cleanup like closing a file handle or a database connection. The two-argument form catch (e, stackTrace) gives you the StackTrace object alongside the exception, useful for logging; if you need to log an error and then let it continue propagating, use a bare rethrow rather than throw e, because rethrow preserves the original stack trace pointing to where the error actually originated, while throw e creates a new stack trace pointing to the rethrow line, hiding the real cause.

🏏

Cricket analogy: A finally block is like ground staff covering the pitch after play regardless of the result — win, loss, or rain-abandoned. rethrow is like a referee escalating an incident to the ICC with the original umpire's report attached, not a fresh report that loses the original context.

Custom Exceptions and Async Error Handling

Custom exception classes are typically simple: class InsufficientFundsException implements Exception { final String message; InsufficientFundsException(this.message); }, giving callers a specific, catchable type instead of a generic string error. Inside async functions, errors thrown by an awaited Future surface exactly like synchronous exceptions and are caught the same way with try/catch around the await; for global, last-resort error handling of uncaught async errors (for example, in a Flutter app), you can run your app inside a custom Zone with runZonedGuarded, which intercepts errors that escape every local try/catch.

🏏

Cricket analogy: A custom InsufficientFundsException is like a specific 'timed out' dismissal rule rather than a vague 'given out' — it names exactly what happened, and runZonedGuarded is like the third umpire system catching any decision that slips past the on-field umpires, a last-resort safety net for the whole match.

Writing a bare catch (e) { } with no on clause catches everything, including Error subclasses like StateError or RangeError that usually indicate a real bug in your code. Swallowing those silently hides problems that should surface loudly during development and testing.

  • throw accepts any non-null object; on SpecificType catch(e) targets a particular exception type.
  • Chain multiple on clauses, falling back to a general catch(e) as a last resort.
  • Exception = expected/recoverable; Error = programming bug that should be fixed, not swallowed.
  • finally always executes, making it the place for cleanup like closing resources.
  • Use rethrow (not throw e) to preserve the original stack trace when re-raising a caught exception.
  • Custom exception classes implementing Exception give callers a specific, catchable type.
  • Avoid bare catch(e) without an on clause — it can silently swallow real bugs signaled by Error subclasses.

Practice what you learned

Was this page helpful?

Topics covered

#Programming#DartStudyNotes#ExceptionHandlingInDart#Exception#Handling#Dart#Throwing#ErrorHandling#StudyNotes#SkillVeris