Skip to content

Errors and Control Flow

Chris Hibbert edited this page Aug 26, 2022 · 1 revision

First a bit of terminology: There are two ways to use an error to indicate something went wrong: throw, or using the error as a rejected-promise-reason. When we don't care to distinguish these, I'll use the term "raise error" to mean both.

To grok this question in its fullness, we must first distinguish two possible purposes of the information carried by a raised error.

  • Something went wrong. To provide an out-of-band human programmer with diagnostic information to help them diagnose a possible bug in some code somewhere.

  • To enable code to test-and-branch on something, such that subsequent non-diagnostic computation proceeds differently depending on this information carried by the error. We need to distinguish two subcases:

    • Use to express non-local non-erroneous control flow
    • To react differently to different kinds or severities of what went wrong. Depending on what kind of thing went wrong, we may wish to take more severe action programmatically, like terminating a vat or shutting down the chain.

One unfortunate feature of JavaScript creates an additional complication, which we cope with by using another unfortunate feature of JavaScript.

  • JavaScript has no non-local non-erroneous exit operation suitable for use as non-erroneous control-flow.
  • JavaScript allows non-errors to be raised (thrown or used as promise rejections).

For non-local non-erroneous control flow, our recommended practice (which we've only recently decided on and started practicing) is that normal non-erroneous control flow, not indicating a problem that should be brought to human attention, should happen by raising a non-error. Then, the code that catches something and branches is not asking "Is this an error I expect or something else?" but rather "Is this a non-error I expect or something else?". In both cases, if it is something else the normal reaction will be to rethrow as if the intermediary were not in the way.

Let's take the prescriptive stance that, for production code, only the "Is this a non-error I expect or something else?" conditional is legitimate. Non-diagnostic production code should never have a conditional of the form "Is this an error I expect or something else?"

This leaves the final case, where different kinds of things going wrong should result in different severities of recovery action. For these, generally, the answer is to report the error in some way other than raising it, such as a call to zcf.shutdownWithFailure(). An important cross-over case is what happens if an error is thrown during the execution of an atomic block after the commit point. In this case, the catcher escalates the severity of a thrown error into a shutdown. But the catcher does this escalation unconditionally --- again, without inspecting the information conveyed by the error.

Footnote: source

Clone this wiki locally