Notes on this diagram:
- An “unrecoverable error” is one that can’t be resolved at the current level. It will become some other sort of error at a different level; the limit of this is “the system barfs an error at the user instead of doing something”.
- A VM error can mean “the program errored out”, i.e. by reaching a trap, or “the program was aborted”, i.e., by gas limiting out. It can also fail by doing an unconditional scry that can’t succeed, or, in the future, by succeeding but having a type error at runtime (the value was not a subtype of the expected type).
- VM errors are basically unrecoverable. They become errors like “transaction failed: out of gas” or something in the transaction subsystem (which knows how to handle that, as it happens: record a failed transaction in the current block).
- If you get a malformed transaction (e.g., a resource machine transaction that violates resource machine invariants) this is a distinct condition (the VM/your program never failed), but it has the same result as the above.
- Intermittent network failures and some unexpected errors aren’t quite unrecoverable: you can retry, in case it was a fluke. At some point you have to give up (block replay gives up after one attempt, network failure has to be tuned somehow). A consensus can keep retrying with backoff, or similar.
- Errors that aren’t recovered in-system, and even ones that are, end up before a user’s eyes at some point; this means they’re sent to the local domain. This runs code so it can have its own set of unexpected or unrecoverable errors; however, you can provide more code as an event handler to handle these (but of course, this code might also fail!). The ultimate sink is always “tell the user something failed”, highlighted in green here.