Exceptions and Error Handling
Programs fail. A file might be missing, a number might not parse, a network request might time out. Error handling is how a program deals with those failures on purpose, instead of crashing. In Dart, the main tools are exceptions (objects that describe a problem) and try-catch blocks (code that watches for those problems).
Throwing and Catching
When something goes wrong, code can throw an exception. By default, an exception travels up until someone catches it. If nobody does, the program stops with an error message.
void checkAge(int age) {
if (age < 0) {
throw FormatException('Age cannot be negative: $age');
}
print('Age is $age');
}
void main() {
try {
checkAge(-5);
} catch (e) {
print('Caught a problem: $e');
}
print('Program continues normally.');
}The try block holds risky code. The catch block runs only if something is thrown. After the catch, the program keeps going.
Catching Specific Types
You can catch only the kinds of problems you expect, and let others pass through:
try {
final n = int.parse('not a number');
print(n);
} on FormatException catch (e) {
print('That was not a number: $e');
}on FormatException means "catch this type only." A general catch (e) catches everything, so prefer the specific form when you can.
finally: Cleanup That Always Runs
Sometimes you need to do something whether or not an error happened, like closing a file. Put that in a finally block:
try {
print('working...');
throw StateError('something broke');
} catch (e) {
print('handled: $e');
} finally {
print('this always runs');
}Exceptions vs Errors
Dart splits failures into two families. Exception means something your program should anticipate and recover from, like bad input. Error means a bug or a broken state, like using a null value you promised would not be null. You usually catch exceptions; you usually fix errors by changing code.
A Worked Example
Here is a small program that combines these ideas: it validates input, throws a clear message on bad data, and handles it gracefully.
double parsePrice(String text) {
final value = double.tryParse(text);
if (value == null || value < 0) {
throw FormatException('"$text" is not a valid price');
}
return value;
}
void main() {
for (final input in ['19.99', 'oops', '-3']) {
try {
print('OK: ${parsePrice(input)}');
} on FormatException catch (e) {
print('Rejected: $e');
}
}
}Notice double.tryParse: it returns null instead of throwing, which is often a friendlier style when failure is normal and expected. Dart lets you choose: throw and catch for problems worth reporting, or null returns for quiet, everyday misses.
Exercise
What is the difference between catch (e) and on FormatException catch (e)?
Show answer
catch (e) catches every thrown object of any type, while on FormatException catch (e) catches only FormatException objects and lets all other errors continue upward. Being specific is safer because it does not accidentally hide unrelated bugs.
Exercise
When does a finally block run?
Show answer
Always, after the try block finishes — whether the try succeeded, an exception was caught, or an exception escaped. That makes it the right place for cleanup like closing files or connections.
Spotted a mistake, or code that does not work?