Exceptions¶
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:
Builtin Errors¶
| Exception type | Thrown on | 
|---|---|
| GeneralError | Calls to errorandabort | 
| ArithmeticError | Arithmetic errors like overflows and division by zero | 
| PreconditionFailed | ensurestatements that returnFalse | 
| AssertionFailed | Failed assertcalls (or other functions fromDA.Assert) | 
Note that other errors cannot be handled via exceptions, e.g., an exercise on an inactive contract will still result in a transaction abort.
User-Defined Exceptions¶
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
message function.
exception MyException
  with
    field1 : Int
    field2 : Text
  where
    message "MyException(" <> show field1 <> ", " <> show field2 <> ")"
Throwing Exceptions¶
There are two ways to throw exceptions:
- Inside of an ActionlikeUpdateorScriptyou can usethrowfromDA.Exception. This works for anyActionthat is an instance ofActionThrow.
- Outside of ActionThrowyou can throw exceptions usingthrowPure.
If both are an option, it is generally preferable to use throw
since it is easier to reason about when exactly the exception will get
thrown.
Catching Exceptions¶
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 create of T will be rolled back and
the first catch clause applies which will create an Error
contract.
         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"