Exceptions are a Daml feature which provides a way to handle certain errors that arise during interpretation instead of aborting the transaction, and to roll back the state changes that lead to the error.
There are two types of errors:
|Exception type||Thrown on|
||Arithmetic errors like overflows and division by zero|
Note that other errors cannot be handled via exceptions, e.g., an exercise on an inactive contract will still result in a transaction abort.
Users can define their own exception types which can be thrown and caught. The definition looks similar to templates, and just like with templates, the definition produces a record type of the given name as well as instances to make that type throwable and catchable.
In addition to the record fields, exceptions also need to define a
exception MyException with field1 : Int field2 : Text where message "MyException(" <> show field1 <> ", " <> show field2 <> ")"
There are two ways to throw exceptions:
- Inside of an
Scriptyou can use
DA.Exception. This works for any
Actionthat is an instance of
- Outside of
ActionThrowyou can throw exceptions using
If both are an option, it is generally preferable to use
since it is easier to reason about when exactly the exception will get
Exceptions are caught in try-catch blocks similar to those found in
languages like Java. The
try block defines the scope within which
errors should be handled while the
catch clauses defines which
types of errors are handled and how the program should continue. If an
exception gets caught, the subtransaction between the
try and the
the point where the exception is thrown is rolled back. The actions
under rollback nodes are still validated, so, e.g., you can never
fetch a contract that is inactive at that point or have two contracts
with the same key active at the same time. However, all ledger state
changes (creates, consuming exercises) are rolled back to the state
before the rollback node.
Each try-catch block can have multiple
catch clauses with the
first one that applies taking precedence.
In the example below the
T will be rolled back and
catch clause applies which will create an
try do _ <- create (T p) throw MyException with field1 = 0 field2 = "42" catch (MyException field1 field2) -> create Error with p = p msg = "MyException" (ArithmeticError _) -> create Error with p = p msg = "ArithmeticError"