A future can complete in the error state. Future.error(...) produces one directly, and .catchError(fn) runs fn on the error to produce a fresh value. Because both branches use already-completed futures, the example has no timers or external events.

Program

Play the program to await one successful call and one recovered call.

future_error.dart
Future<int> parseScore(String input) {
  if (input == 'ok') return Future.value(9);
  return Future.error(FormatException('not a score'));
}

Future<int> safeScore(String input) {
  return parseScore(input).catchError((_) => 0);
}

Future<void> main() async {
  var good = await safeScore('ok');
  var bad = await safeScore('???');
  print('good=$good bad=$bad');
}
Future.error `Future.error(...)` is an already-completed failure; awaiting it throws.
catchError `.catchError(fn)` runs `fn` on the error and returns a fresh future carrying its result.
await unwraps `await safeScore(...)` yields the recovered value, so `main` never sees an exception.