Search code examples
fluttererror-handling

Approach to and difference between throwing errors and exceptions in Flutter/Dart


Could someone please take 5 minutes and explain the difference between throwing errors and exceptions in Flutter/Dart. The information I find online is very contradictory and confusing.

  1. In which cases should I throw an Error, in which cases should I throw an exception?

  2. Which of those can or should be caught by try/catch approach?

  3. What is the difference between throwing the difference error types, for instance an Error or an ArgumentError? Where can I find a list of these types?

Many thanks in advance!


Solution

    1. In which cases should I throw an Error, in which cases should I throw an exception?

    Errors are for logical errors made by the programmer. They are for conditions that should not logically occur. Throw them for preventable situations that shouldn't happen if code properly follows API contracts.

    Exceptions are for runtime errors. Throw them for situations that are out of your control (such as user input, responses from servers, unexpected data when reading a file, ...).

    From the documentation:

    An Error object represents a program failure that the programmer should have avoided.

    Examples include calling a function with invalid arguments, or even with the wrong number of arguments, or calling it at a time when it is not allowed.

    These are not errors that a caller should expect or catch — if they occur, the program is erroneous, and terminating the program may be the safest response.

    and from Effective Dart:

    DO throw objects that implement Error only for programmatic errors

    The Error class is the base class for programmatic errors. When an object of that type or one of its subinterfaces like ArgumentError is thrown, it means there is a bug in your code. When your API wants to report to a caller that it is being used incorrectly throwing an Error sends that signal clearly.

    Conversely, if the exception is some kind of runtime failure that doesn't indicate a bug in the code, then throwing an Error is misleading. Instead, throw one of the core Exception classes or some other type.

    One reason why the distinction matters is that in principle, an optimizer could remove Error conditions from the code since they should not logically occur and therefore can be considered to be dead code.

    A few rules of thumb:

    • Would your program in principle (in the absence of other bugs) behave the same if the throwing code didn't exist? If so, then use an Error.

    • Do you expect the thrown object to be caught? Do you expect something in the callstack to be able to recover? If so, then use an Exception.

    Now, there are some cases that could be considered gray areas. For example, a user could enter invalid input that is passed to a function that cannot handle it. Is that a logical error or a runtime error? Whose responsibility is it to check for that invalid input, the caller's or the callee's? It depends on the contract of the function. Does its API documentation state that it must not be called with certain inputs? If so, then the caller is responsible for verifying those inputs first.

    1. Which of those can or should be caught by try/catch approach?

    You should not catch Errors. From Effective Dart:

    DON'T explicitly catch Error or types that implement it

    This follows from the above. Since an Error indicates a bug in your code, it should unwind the entire callstack, halt the program, and print a stack trace so you can locate and fix the bug.

    Catching errors of these types breaks that process and masks the bug. Instead of adding error-handling code to deal with this exception after the fact, go back and fix the code that is causing it to be thrown in the first place.

    That's also why you should avoid catch blocks without on clauses; such catch blocks will catch everything.

    1. What is the difference between throwing the difference error types, for instance an Error or an ArgumentError? Where can I find a list of these types?

    There is not going to be any exhaustive list of all Error types since anybody can make their own custom class that implements Error. You can see a list of the built-in Error types by looking at the Error documentation and looking at its "Implementers" list.