Collectible Errors?!

by Jul 16, 2022AL Language

Home 9 Development 9 AL Language 9 Collectible Errors?!

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;

Recent Articles from the category

BC Open Source? How to start?

BC Open Source? How to start?

BC Open Source? How to start? One of the most exciting news introduced last month in Lyon during Directions EMEA 2023 was the changes to the open-source initiative. This means that you can now contribute to the source code of the Base app and the System app, which are...

read more
Validate a FlowField Field. Wait? What?

Validate a FlowField Field. Wait? What?

Validate a FlowField Field. Wait? What? There are not many things in the AL Language that surprised me. However, last week, I found one such thing - I reviewed customizations made by another partner and had to analyze the OOTB code of the Demand Forecast matrix. I run...

read more
Dynamics NAV 2013 & Expired Cronus License

Dynamics NAV 2013 & Expired Cronus License

We found an interesting problem - we were not able to run the development environment for Dynamics NAV 2013. Whenever we tried to run the development client, we got the following error message: "Your program license has expired" and the development client has closed...

read more
Indirect Dependencies and Access Modifiers

Indirect Dependencies and Access Modifiers

Last week, there was a discussion on Yammer on how to get values from the "Sent Email" record when all fields are marked as Internal. I was surprised that many people do not know what can/can't access modifiers (such as local, protected, or internal) be used for. I...

read more
AL Extensions: Replace Document Attachment

AL Extensions: Replace Document Attachment

I have published a new simple, open-source extension that allows replacing existing document attachments in all master entities as well as in open documents. The source code as well as the app file that can be installed in your environment is available on my GitHub...

read more

Sign Up for News

Certifications

Highest certification
Microsoft Data Management and
also in D365 Business Central

Microsoft Certified: Dynamics 365 Business Central Functional Consultant Associate

See other certifications here