Collectible Errors?!
It has been already almost a year since ErrorInfo datatype & CollectibleErrors were introduced (I already have an article about basic structure: ErrorInfo data type & Collectible Errors). This article was released for the first time in September 2021 before this functionality was publicly available. Since then, there have been some changes and fixes I have already added to the original article.
Today, I want to look at some examples and see how to use collectible errors, how it differs from standard errors we have known for years, and how to use it. Later (probably in two separate articles in the upcoming weeks), I’ll show you more advanced examples of collectible errors (with the rest of the parameters) and some examples of actual usage of collectible errors and also how (and where) it is already used in the Base App.
Examples
Standard Errors
Let’s start with a basic example, with the standard error (..) procedure we all know and have used for ages. The example below is easy; calling the procedure will raise the first Error, and the whole process stops. The second Error won’t be executed (and warning AA0136 will be shown).
local procedure Error1()
begin
Error('Test error 1');
Error('Unreachable code.');
end;
In the example below, we get the same result with the ErrorInfo data type. In this type, we use the new Error(ErrorInfo) method (the difference is that the “old” method accepts text as a parameter, this new, overloaded method accepts ErrorInfo data type). But the result is absolutely the same – the first Error is raised once the method is called, and the second Error is unreachable.
local procedure Error2()
begin
Error(ErrorInfo.Create('Test error 2'));
Error(ErrorInfo.Create('Unreachable code.'));
end;
Collectible Errors
To start using collectible errors, we have to be familiar with two terms.
The procedure that should use CollectibleErrors needs to be decorated with the ErrorBehavior attribute ([ErrorBehavior(Behavior: ErrorBehavior)]). Right now, the only value available for ErrorBehavior enum is Collect. So if you want to enable collectible errors for a specific method, write [ErrorBehavior(ErrorBehavior::Collect)] before the procedure. If you do not want collectible errors (= want the standard error behavior when the error is raised once the line with Error is executed) simply do not use this parameter.
However, even with this parameter, errors will still be raised once the code executes them. So the example below is still the same even with [ErrorBehavior(ErrorBehavior::Collect)] and both Error(Text) and Error(ErrorInfo) methods work in the same way.
[ErrorBehavior(ErrorBehavior::Collect)]
local procedure Error3()
begin
Error(ErrorInfo.Create('Test error 3'));
Error(ErrorInfo.Create('Unreachable code.'));
end;
// OR
[ErrorBehavior(ErrorBehavior::Collect)]
local procedure Error3B()
begin
Error('Test error 3');
Error('Unreachable code.');
end;
So what’s the second term I said you need to be familiar with?
It’s “Collectible“! Because only errors marked as Collectible are collected in methods defined with [ErrorBehavior(ErrorBehavior::Collect)]. How to define Error as collectible? It’s easy, but there is a difference between Error(Text) and Error(ErrorInfo) methods – only the second one, Error(ErrorInfo), can be set as Collectible. It’s the second parameter in ErrorInfo.Create() method just after the text of the Error.
The example below shows how to use collectible errors properly.
[ErrorBehavior(ErrorBehavior::Collect)]
local procedure Error4()
begin
Error(ErrorInfo.Create('Test error 4', true));
Error(ErrorInfo.Create('Test error 5', true));
end;
And how does it look from the user’s side? It seems like a standard error – the user sees there is something wrong. The message is also pretty clear – “Multiple errors occurred during the operation, the first of which is: Test error 4. All errors can be found under ‘Detailed Information’.“. As stated, under detailed information, all errors can be found. Below is the log from the error (it’s what you get once you use the “Copy information to clipboard” link in error).
/*
Multiple errors occurred during the operation the first of which is: Test error 4. All errors can be found under 'Detailed Information'.
Test error 4
Test error 5
Internal session id:
5296a108-3f8a-4514-ae4f-a54d9a2e1947
Application Insights session id:
ce4a57d4-1172-470c-bcee-381882638a19
Client activity id:
0282fb25-8950-6759-064d-01cb9db6a4ab
Timestamp:
2022-07-15T08:31:28.2756526Z
AL call stack:
"TKA Collectible Errors Mgt."(CodeUnit 69000).Error4 line 5 - Collectible Errors by Kepty.cz, Ing. Tomas Kapitan
"TKA Collectible Errors Mgt."(CodeUnit 69000).Try line 11 - Collectible Errors by Kepty.cz, Ing. Tomas Kapitan
"TKA Customer List"(PageExtension 69900).OnOpenPage(Trigger) line 4 - Collectible Errors by Kepty.cz, Ing. Tomas Kapitan
*/
The last thing I want to cover in this article is the TestFields() method. Even this method can be used as Collectible. You are definitely familiar with the method Record.TestField(Field, [Value]) where [Value] is the value the field should have. If omitted, the method tests that the Field is not blank.
The error from this method (as the example below) can not be collected, so the error in the example is unreachable!
[ErrorBehavior(ErrorBehavior::Collect)]
local procedure Error5()
var
Customer: Record Customer;
begin
Customer.FindFirst();
Customer.TestField(Name, '');
if Customer.Name <> '' then
Error(ErrorInfo.Create('Unreachable code.', true));
end;
To be able to collect TestFields() errors, we need to use the new method Record.TestField(Field, Value, ErrorInfo) or Record.TestField(Field, ErrorInfo). These two methods accept as the last parameter instance of ErrorInfo and use the setting from this ErrorInfo. That means, if ErrorInfo is set as Collectible, the TestField() itself is set as Collectible.
To create a new collectible instance of ErrorInfo, we can set it manually (MyErrorInfo.Collectible := true) or use ErrorInfo.Create() method (WITHOUT ANY PARAMETER!) which returns an empty ErrorInfo object that is set as Collectible.
One thing to remember – ErrorInfo.Create() is by default set as Collectible, however, ErrorInfo.Create(‘My own error’) is by default set as NonCollectible and you need to specify second parameter ErrorInfo.Create(‘My own error’, true) to create ErrorInfo as collectible!
[ErrorBehavior(ErrorBehavior::Collect)]
local procedure Error6()
var
Customer: Record Customer;
begin
Customer.FindFirst();
Customer.TestField(Name, '', ErrorInfo.Create());
if Customer.Name <> '' then
Error(ErrorInfo.Create('This error will be raised all times when TestField error will be raised.', true));
end;