2

I'm having trouble working out how I can catch exceptions from isolates. With the following code, I've tried everything I can think of to handle the error that is thrown from task, but it's still marked as Unhandled exception.

void main() async {
  try {
    var rp = ReceivePort();

    rp.listen((message) {
      print(message);
    }, onError: (e) {
      print(e);
    });
    rp.handleError((e) {
      print(e);
    });
    var isolate = await Isolate.spawn(task, rp.sendPort);
    isolate.addErrorListener(rp.sendPort);
  } catch (e) {
    print(e);
  }
}

void task(SendPort sp) {
  sp.send('hello from isolate');
  throw Exception();
}

What am I missing here?

EDIT: From the below answer, my solution should look like this:

void main() async {
  var rp = ReceivePort();
  var errorRp = ReceivePort();
  errorRp.listen((e) {
    print('exception occured');
    print(e);
  });
  rp.listen(print);
  await Isolate.spawn(task, rp.sendPort, onError: errorRp.sendPort);
  await Future.delayed(Duration(seconds: 1));
  rp.close();
  errorRp.close();
}

void task(SendPort sp) {
  sp.send('hello from isolate');
  throw Exception();
}

テッド
  • 776
  • 9
  • 21

1 Answers1

3

I think you have missed an important segment of the documentation for Isolate.spawn:

You can also call the setErrorsFatal, addOnExitListener and addErrorListener methods on the returned isolate, but unless the isolate was started as paused, it may already have terminated before those methods can complete.

You issue is that your spawned Isolate has already executed throw Exception() before your isolate.addErrorListener(rp.sendPort); has been executed.

Instead, do something like this to spawn the Isolate as paused and then later resume it:

    var isolate = await Isolate.spawn(task, rp.sendPort, paused: true);
    isolate.addErrorListener(rp.sendPort);
    isolate.resume(isolate.pauseCapability!);

Alternative, you can just give the Isolate your error handler as part of spawning:

var isolate = await Isolate.spawn(task, rp.sendPort, onError: rp.sendPort);
julemand101
  • 28,470
  • 5
  • 52
  • 48
  • Thank you for your response. I did see this in the docs and played around with it, but it didn't seem to have any effect. I've edited my code above so you can see what I have now. `print('Handling exception')` is never reached. – テッド Jan 13 '22 at 16:19
  • What do you mean by not having an effect? You are printing the error message including stacktrace so you are still going to see it in your terminal output. But you have caught it. – julemand101 Jan 13 '22 at 16:20
  • Just edited my message above for clarity – テッド Jan 13 '22 at 16:21
  • Ok. But notice that the exception are not going to be sent as an error to the `Stream` from `ReceivePort`: https://api.dart.dev/stable/2.15.1/dart-isolate/Isolate/addErrorListener.html "The errors are sent back as two-element lists. ". The reason is really that `SendPort` does not have concept of sending an error back. It can only send `Object`. – julemand101 Jan 13 '22 at 16:22
  • I would recommend you create a dedicated `ReceivePort` for handling error messages (unless you just want to assume list objects with two objects are errors) :) – julemand101 Jan 13 '22 at 16:23
  • ahhhhh I think I understand. The `onError` handler for `rp.listen` does not catch errors. Instead the error will come through in `listen`'s `onData` callback. Is this correct? – テッド Jan 13 '22 at 16:26
  • @テッド Correct. You just provide it an `SendPort` which should be used to send errors to the `ReceivePort` linked to that `SendPort`. But `SendPort` can only send objects (and does not have any concept of sending errors) so the errors are going to be received as normal objects seen from the perspective of the `ReceivePort`. – julemand101 Jan 13 '22 at 16:28
  • Understood. Many thanks! I will probably do as you say and create a dedicated `ReceivePort` for errors. You can see why it is a little confusing there being an `onError` param for the `listen` method. – テッド Jan 13 '22 at 16:30
  • 1
    I fully understand where the confusion comes from. Another thing. Please remember to close your `ReceivePort` objects when you are done since they will prevent the isolate (they are part of) from being stopped the natural way. Also, `ReceivePort` objects are not being freed unless you `close()` them since Dart does not track if all `SendPort` objects linked to a `ReceivePort` is gone across isolates. – julemand101 Jan 13 '22 at 16:32
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/241039/discussion-between--and-julemand101). – テッド Jan 13 '22 at 16:52