About error handling in stream section

About using try-catch approach for error handling in Chapter 13: Streams.
The code is:

try {
  final file = File('assets/text_long.txt');
  final stream = file.openRead();
  await for (var data in stream) {
    print(data.length);
  }
} on Exception catch (error) {
    print(error);
} finally {
  print('All finished');
}

Then, the book says, “In this example, you’re catching all exceptions.”. However, I find that it isn’t the case. See the code below:

Future<void> main() async {
  try {
    final file = File('assets/text_long.txt');
    final stream = file.openRead();
    await for (var data in stream) {
      print(data.length);
      throw ArgumentError();
    }
  } on Exception catch (error) {
    print(error);
  } finally {
    print('All finished');
  }
}

Interestingly, after running, the console says I have unhandled exception.
I have to change the code (delete on Exception):

Future<void> main() async {
  try {
    final file = File('assets/text_long.txt');
    final stream = file.openRead();
    await for (var data in stream) {
      print(data.length);
      throw ArgumentError();
    }
  } catch (error) {
    print(error);
  } finally {
    print('All finished');
  }
}

Not really quite understand why. I feel that this is because ArgumentError don’t belong to the class Exception, therefore the exception is not handled in the first case. The second case infer the type of error as Object and it can catch anything.

However, if it is true, a second question arise… Isn’t all exceptions implement the Exception class? Even when we need to write a custom error, we need to implement the Exception class.

I should add more explanation about this in the next edition of the book, either in this chapter or in the Error Handling chapter.

Here are a points to note:

  • Errors and Exceptions are not the same thing, even though people (and even Dart itself) use the terms interchangeably in common speech.
  • An exception is an exceptional situation that you as the programmer should plan for. For example, the file you are trying to open might not exist. What are you going to do in that case? It’s not an error that the file doesn’t exist. It’s just a situation you need to handle. You can (and should) catch and handle such a situation. Using a try/catch block is one way to do that.
  • An error, on the other hand, indicates a bug in the code. It’s problem with the logic of the program and it’s the programmer’s fault, not the user’s fault. You shouldn’t “handle” errors with try/catch blocks. Rather, you should just let them crash your app and then go fix your bug so that it doesn’t happen again. For example, if you have a depositMoney(int amount) method that expects positive values for amount, then this method might rightly throw an ArgumentError if amount is negative. After the app crashes, you should go fix whatever code is passing a negative value to depositMoney.

So technically, on Exception does catch all of the exceptions. That is, it catching everything that is a subclass of Exception. The thing is ArgumentError is a subclass of Error, not Exception, so on Exception didn’t catch it. I’m not sure why Dart calls the error an unhandled “exception”. But if you throw 'tomatoes', Dart will call that an unhandled exception too. Removing on Exception will catch everything, including errors. That’s probably not what you want to do, though. Catch exceptions. Fix errors.

2 Likes

Yes, I understand now. I somehow remember the book has explained the difference between exception and error in the chapter of error handling, but not as detailed in this answer.

1 Like