If a future completes with an error before a catchError callback is registered, it’s seemingly impossible to catch that error.
If this is in fact the intended behavior, I don’t see this documented anywhere. Adding something akin to “Unlike Future.then, callbacks registered with Future.catchError will not be executed if they are registered after the future has already completed” to the documentation for Future would be helpful.
See this example program:
import 'dart:async';
void main() {
try {
completeError();
} catch (error) {
// Doesn't run.
print('caught in main');
}
}
Future completeError() async {
var completer;
try {
completer = new Completer<bool>();
completer.completeError('error');
} catch(error) {
// Doesn't run.
print('caught');
}
try {
await new Future.delayed(const Duration(seconds: 1));
completer.future.catchError((error) {
// Doesn't run.
print('caught error: $error');
});
} catch (e) {
// Doesn't run.
print('caught');
}
}
Output:
Unhandled exception:
error
#0 _rootHandleUncaughtError.<anonymous closure> (dart:async/zone.dart:1146)
#1 _microtaskLoop (dart:async/schedule_microtask.dart:41)
#2 _startMicrotaskLoop (dart:async/schedule_microtask.dart:50)
#3 _runPendingImmediateCallback (dart:isolate-patch/isolate_patch.dart:96)
#4 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:149)
There is no assert.
It’s unfortunately not that easy to add an
assert
tocatchError
without too many false positives. Futures may get reused, in which case they are (on purpose) already completed, or they might just be completed immediately.What would have helped (I guess): a zone that just logs, and then triggers your
catchError
.In this example you will see the “zone error” message, but you still get the normal
catchError
notification. This would have probably led you on the right tracks.