skip to content

In Dart, what is the difference between Exception and Error, and when is catching an Error the wrong move?

level: middleimportance: must knowfreq 62%

answer

  1. bug versus anticipated failure
  2. Error: fix the calling code
  3. Exception is a marker interface
  4. extending Error records stackTrace
  5. avoid_catching_errors lint

basics

~20 s

In Dart, an Error such as ArgumentError or StateError signals a bug the programmer should have prevented; an Exception such as FormatException signals a failure a correct program still meets. Catching an Error hides the bug instead of fixing it.

solid answer

~50 s

`Error` is the base class for programming failures: a violated precondition (`ArgumentError`, `RangeError`), a call at the wrong time (`StateError`), a failed cast (`TypeError`), a failed `assert` (`AssertionError`), plus system failures such as `StackOverflowError`. The caller could have avoided it, so the fix belongs in code. `Exception` is a marker interface for conditions a correct program still meets — malformed input (`FormatException`), I/O (`FileSystemException`), a `TimeoutException` — meant to be caught and to carry useful fields. Apart from the stack trace an extending `Error` subclass records, the split is a convention: implementing `Exception` only documents intent, and anything non-null can be thrown. Catching an `Error` is wrong in ordinary code because it turns a crash that points at the bug into silent wrong behaviour; the narrow exceptions are framework-level boundaries that log and isolate failures, and tests of your own code.

go deeper

for a junior

Recall one line each: Error means a bug to fix, Exception means a failure to handle, and name two SDK examples of each.

for a middle

Explain the precondition rule for choosing between them, that Exception is only a marker, and why extending Error records a stack trace.

for a senior

Show the policy: never catch Error in app logic, prefer validation to catching, and allow broad catches only at boundaries that log or rethrow.

for a principal

Weigh where a codebase draws its boundaries: which layers may catch broadly, which lints enforce the rest, and how that keeps bugs loud.

## Two families, one throw Dart's core library splits thrown objects into two families, but the language treats them the same: both are thrown with `throw`, caught with `on`/`catch`, and neither is checked. The difference is **what the throw means**. | | `Error` | `Exception` | |---|---|---| | Meaning | a **bug**: the program did something it should have avoided | an **anticipated failure** a correct program can meet | | Who fixes it | the programmer, by changing code | the caller, by handling it at run time | | Expected to be caught? | no | yes | | Kind of type | a class you can extend | an `abstract interface class` you implement | | Stack trace | a subclass that **extends** `Error` records `stackTrace` on its first throw | none stored; read it from `catch (e, st)` | | Examples | `ArgumentError`, `RangeError`, `StateError`, `TypeError`, `UnsupportedError`, `AssertionError` | `FormatException`, `IOException`, `FileSystemException`, `TimeoutException` | The SDK's own documentation of `Exception` says being one "has no other effect than documentation". The distinction is a **contract with the reader**, not a runtime mechanism. ## How to tell which one a failure is The SDK's rule of thumb: if the condition is **detectable before the call** — the docs say the argument "must" or "must not" be something — violating it is an `Error`. `String.contains` documents that `startIndex` must not be negative, so a negative index throws a `RangeError`. If the condition **cannot be checked in advance** — the file vanished, the text is malformed, the server timed out — the callee reports it as an exception, effectively an alternative result the caller must handle. - `int.parse('x')` throws `FormatException`: the caller could not know the text was bad without trying. - `list[10]` on a three-element list throws an `IndexError`, which is a `RangeError`: the caller could check `length` first. - Reading a file that was deleted throws `FileSystemException`: nothing the caller checks beforehand can rule that out. ## Why catching an Error is usually wrong Effective Dart says **don't explicitly catch `Error` or types that implement it**, and the `avoid_catching_errors` lint enforces it. The reasons: 1. **It masks the bug.** An `Error` should unwind the stack and stop the program with a trace pointing at the mistake. A catch turns that into a program that keeps going in a state nobody designed. 2. **It swallows asserts.** A failed `assert` throws `AssertionError`; a catch that covers it makes the assertion vanish in debug runs, where it was meant to stop you. 3. **It catches system failures you cannot handle** — `StackOverflowError` and `OutOfMemoryError` are errors too. 4. **The fix is elsewhere.** Instead of adding a handler after the fact, go back and prevent the condition: check the length, use `int.tryParse`, validate arguments. The same logic is why Effective Dart says to **avoid catches without `on` clauses** (`avoid_catches_without_on_clauses`): a bare `catch (e)` catches every `Error` along with the exceptions you meant. ## Where a broad catch is legitimate - **Boundaries that isolate unrelated work** — framework or low-level code that runs arbitrary callbacks and must survive one failing. Even there, Effective Dart prefers catching `Exception` over catching everything, and insists you **do something** with what you caught: log it, show it or rethrow it. - **Tests of your own code**, which assert that a precondition violation throws the documented `ArgumentError` or `StateError`. What a Flutter app does with errors nobody caught — the framework's error hooks — is a separate subject owned by the Flutter errors topic. ## A quick diagnostic When a stack trace shows an `Error`, ask *which line broke a documented precondition?* When it shows an `Exception`, ask *which caller should have handled this, and did it have an `on` clause for it?* The first leads to a code fix, the second to a handler.

  • In Dart, what is the difference between a class that extends Error and one that only implements it?
    Only a class that extends `Error` gets its `stackTrace` getter filled in automatically the first time it is thrown. A class that merely implements `Error` must provide `stackTrace` itself and gets nothing recorded; `StackOverflowError` and `OutOfMemoryError` are SDK examples that implement it.
  • In Dart, if broad catching is sometimes needed at a boundary, why catch Exception rather than Object?
    `on Exception` still handles the runtime failures a boundary can recover from, while letting `Error`s — bugs, failed asserts, stack overflows — continue to the top where they are reported with their trace. Catching `Object` treats a bug as one more recoverable failure.

An Exception is a closed road on your route: expected now and then, so drivers carry a detour plan. An Error is a car built with the brakes wired backwards: the fix is at the factory, not a better detour.

saying these in an interview costs you the question

  • Error and Exception are interchangeable names for the same thing in Dart.
  • Implementing Exception makes the compiler force callers to catch it.
  • A RangeError from bad input should be caught with on RangeError and skipped.
  • Wrapping main in catch (e) is good practice to keep an app from crashing.
  • Only Exception and Error subtypes can be thrown in Dart.