Skip to main content

Menu

System onlineAI Tutorials / 2026

AI Tutorials
Knowledge search

Recent · Popular · Tutorials

  • dart3 tutorials
  • java1 tutorial
  • Javascript3 tutorials
  • Node.js Introduction2 tutorials
  • dartdart · General · In depth
  • JavascriptJavascript · Advanced · In depth
  • JavascriptJavascript · Advanced · Mid
  • JavascriptJavascript · Advanced · Short

Find a tutorial

Tell the assistant what you want to learn. It suggests published chapters.

For example: “I want to learn Rust ownership from the start.” The conversation is kept only in this tab.

0 / 2,000 characters

Enter to send, Shift+Enter for a new line. Do not include personal information.

Chapter 15

Exceptions and Error Handling

Programs fail. Files go missing, users type nonsense, networks drop. What separates a fragile program from a dependable one is not whether it ever hits problems — it is what happens when it does. In this chapter you will learn how Dart lets you detect problems, respond to them, and clean up after th
Level: GeneralLength: In depth4 min read

Read at another depth

Exceptions and Error Handling

Programs fail. Files go missing, users type nonsense, networks drop. What separates a fragile program from a dependable one is not whether it ever hits problems — it is what happens when it does. In this chapter you will learn how Dart lets you detect problems, respond to them, and clean up after them, so a failure in one place does not silently corrupt the rest of your program.

This chapter assumes you already know how to write Dart functions and use classes. You do not need any prior experience with error handling.

What is an exception?

An exception is an object that represents a problem that happened while your program was running. When a problem occurs, Dart "throws" an exception, which means it creates that object and stops the current flow of code. The exception travels up until some code "catches" it and decides what to do. If nothing catches it, the program stops and prints the error.

Think of it like a fire alarm. Any point in the building can pull the alarm (throw). The alarm sound travels everywhere until someone responsible responds (catches). If nobody responds, the whole building shuts down.

Throwing an exception

You throw an exception with the throw keyword. Dart has a few built-in exception types, and you can also make your own.

Code example in dart
void setAge(int age) {
  if (age < 0) {
    throw ArgumentError('Age cannot be negative, got $age');
  }
  print('Age set to $age');
}

void main() {
  setAge(30);   // works fine
  setAge(-5);   // throws and stops the program
  print('never reached');
}

ArgumentError is a built-in type that means "you passed me a bad argument." When the second call throws, the program prints the error and stops. The final print never runs.

Two built-in types appear constantly:

  • Exception: for expected problems, like a missing file or bad input.
  • Error: for bugs, like using a variable that holds no value. Errors usually mean the code itself is wrong and should be fixed, not caught.

Catching exceptions

To respond to a problem instead of crashing, wrap the risky code in a try block. If an exception is thrown inside try, control jumps to the matching catch.

Code example in dart
void main() {
  try {
    setAge(-5);
    print('This line is skipped when setAge throws');
  } catch (e) {
    print('Caught a problem: $e');
  }
  print('The program continues normally');
}

Run this and you will see the caught message, then the final line. The program recovers instead of stopping.

You can catch specific types by writing on Type. This matters because different problems deserve different responses. A missing file might mean "ask the user for another path," while bad input might mean "show a validation message."

Code example in dart
class OutOfStock implements Exception {
  final String item;
  OutOfStock(this.item);
  @override
  String toString() => 'Out of stock: $item';
}

void order(String item, int stock) {
  if (stock == 0) throw OutOfStock(item);
  print('Ordered $item');
}

void main() {
  try {
    order('coffee', 0);
  } on OutOfStock catch (e) {
    print('Sorry, $e. Try another item.');
  } catch (e) {
    print('Unexpected problem: $e');
  }
}

The on OutOfStock catch (e) branch handles our custom exception. The plain catch (e) is the safety net for everything else. You can also write on OutOfStock with no variable if you do not need the details.

There is a third clause, finally, which runs no matter what happens — success, exception, or rethrow. This is where cleanup belongs, like closing a file or a database connection.

Code example in dart
void main() {
  try {
    print('opening resource');
    throw StateError('something broke');
  } catch (e) {
    print('caught: $e');
  } finally {
    print('closing resource — always runs');
  }
}

Catching and rethrowing

Sometimes you want to record a problem but not fully swallow it. Use rethrow to pass the exception up to whoever called you, after doing your own handling first.

Code example in dart
void saveData() {
  try {
    throw Exception('disk full');
  } catch (e) {
    print('logging the failure: $e');
    rethrow; // let the caller decide what to do next
  }
}

void main() {
  try {
    saveData();
  } catch (e) {
    print('the app shows an error dialog: $e');
  }
}

A common mistake is writing an empty catch block that hides the problem. If you catch an exception and do nothing, your program may keep running with bad data, and the real bug becomes much harder to find later. If you are not going to handle an exception meaningfully, do not catch it at all — or rethrow it.

Making your own exceptions

Custom exception types are just small classes. Making a class implement Exception (or Error for bugs) gives your failures a name your code can catch precisely. The OutOfStock example above is the standard pattern: a class with a field and a helpful toString. Use @override String toString() so the printed message is readable when the exception reaches a log or a crash report.

Try, but do not fear

Dart's analyzer is strict about types, so many potential problems — a variable that might hold nothing, a value of the wrong kind — are caught before your program even runs. Exceptions are for the problems that remain: the outside world. Network calls, file systems, user input, and hardware all misbehave eventually, and try/catch is how you stay in control when they do.

A good habit: throw exceptions from low-level code when something is impossible to continue from, and catch them higher up where you know what the user was trying to do and can respond sensibly.

How this connects to async code

One more thing before you move on: exceptions and asynchronous code need each other. In the next chapter you will meet async and await, and you will see that a plain try/catch block also catches exceptions thrown by awaited asynchronous calls — one of the reasons Dart's approach to async is so pleasant to work with. Everything you learned here carries over directly.

Exercise

Exercise

A function reads a file, and you want to make sure the file is closed whether or not reading succeeds. Where should the closing code go, and why?

Show answer

In the finally clause of a try block. Code in finally runs whether the try block succeeds or throws, so the file is always closed. Putting it only after the try block would be skipped whenever an exception occurs.

Exercise

What is the difference between catch (e) and on OutOfStock catch (e)?

Show answer

on OutOfStock catch (e) catches only exceptions of type OutOfStock, letting other exceptions pass through to other handlers. A bare catch (e) catches every exception. Catching specific types lets you give each kind of problem its own response.

Spotted a mistake, or code that does not work?

Report an issue

Chapter: Exceptions and Error Handling

0 / 2,000 characters

Plain text, for example: the second code sample does not compile. Do not include personal information.

You can't do that