[Ballerina] Error Handling — Part II
In the previous post we looked into the basics of errors and error handling in Ballerina, including the error
type, creating error values, panic
, trap
, check
, checkpanic
, etc.
In this post we will be looking at a bit more advanced concepts, including
- defining and using custom error types
- immutability of errors and the type test for errors
Defining Custom Error Types
While working with the generic error type is straightforward, quite often you would want to be more precise in declaring what kinds of errors your function(s) would return.
Moreover, while you could literally specify any string as the error reason, when it comes to real world scenarios, there would usually be a predefined set of error reasons.
Additionally, while the generic error type’s detail mapping is an open record with optional fields, you may want to mandate an error detail mapping to contain additional information (e.g., an error code).
A custom error type can be defined by specifying a reason type and optionally a detail type.
- the reason type should be a subtype of the
string
type - the detail type if not specified would default to
record {|
string message?;
error cause?;
(anydata|error)...;
|};
If specified, it should be a record that is a subtype of the above.
Let’s now define a custom error with a finite set of strings as possible reasons and a custom record as the detail record.
An error of the type FileDeletionError
, defined on L16, can only be defined with either “FileNotFound” or “InsufficientPermission” as the reason. Anything else will result in a compilation error. Moreover the message
and code
fields are also mandatory now for the error detail.
The different error scenarios in the above example could also have had their own errors.
Errors are Immutable
Any and all error values in Ballerina are immutable. Thus when you access the reason, detail, or stacktrace of an error you know it has not been modified after creation. Additionally, if you try to update an error, it would result in a panic!
This also means, the type test is always an “is-like” check for an error value. You can define custom errors, use indirect error constructors, etc., but when the type test is carried out, the only checks are if the error value’s reason and detail types belong to those of the type tested against.
This basically means that it does not matter where, when, or how an error was created, the error value would belong to a particular error type as long as the reason and detail mapping belong to the expected types.
That’s it for this post! :)
In the next post, we will look at error binding patterns and match patterns.