Sometimes, it’s good to remember the old tricks while we’re showered in new fancy stuff. This video is about catching errors and how to do that without TryFunction or collectable errors, check it out:

In this video, Erik demonstrates one of the oldest error-handling techniques in AL (and its predecessor C/AL) — a trick that dates back roughly 30 years to the early days of Navision. While modern AL offers try functions and collectible errors, this foundational pattern remains useful and is sometimes overlooked by newer developers.
The Setup: A Problematic Codeunit
Erik starts by creating a simple codeunit that deliberately throws an error. This represents any codeunit in your system that might fail during execution:
codeunit 50100 "Problematic Code Unit"
{
trigger OnRun()
begin
Error('Big problems');
end;
}
He then creates a page extension on the Customer List with an action to invoke this codeunit. Running it normally results in the expected behavior — the error is thrown, execution stops, and the user sees “Big problems” as an error dialog.
The Oldest Trick in the Book: Wrapping Codeunit.Run in an IF Statement
Here’s the core technique: instead of calling Codeunit.Run as a standalone statement, you wrap it in an IF statement:
if cu.Run() then
Message('Success')
else
Message('Something happened');
By encapsulating Codeunit.Run inside an IF statement, the error is not thrown to the user and execution is not stopped. Instead, when an error occurs anywhere within the scope of the codeunit’s execution, control returns to the IF statement with a return value of false. The code gracefully continues into the ELSE branch.
Stepping Through in the Debugger
Erik demonstrates this with the debugger by setting a breakpoint on the IF statement and stepping into the codeunit call (F11). You can see:
- Execution enters the codeunit’s
OnRuntrigger - The
Error('Big problems')line is hit - Instead of stopping, execution returns to the
IFstatement Codeunit.Runreturnsfalse- The
ELSEbranch executes — “Something happened” is displayed
Retrieving the Error Details
The natural follow-up question is: what error actually occurred? AL provides several built-in functions for this:
GetLastErrorCode()— returns an error code (often “Dialog”, which may not be very helpful)GetLastErrorText()— returns the full error message textGetLastErrorCallStack()— returns the call stack at the point of the error
if cu.Run() then
Message('Success')
else
Message('Something happened: %1', GetLastErrorText());
Running this now shows: “Something happened: Big problems” — the exact text from the Error call inside the codeunit.
The Transaction Caveat
There is an important limitation to be aware of: this technique does not work when there is an open write transaction.
Erik demonstrates this by adding a Rec.Modify() call before the IF Codeunit.Run statement. This opens a write transaction, and when you then try to use the IF Codeunit.Run pattern, you get an error message explaining that Codeunit.Run is only allowed in a write transaction if the return value is not used.
The reason is that wrapping Codeunit.Run in an IF statement initializes a new transaction, and Business Central does not support nested transactions — you can only have one active transaction at any given time.
The solution: commit before calling the codeunit:
if Rec.FindSet() then
repeat
// Do something with Rec
Commit();
if not cu.Run() then begin
Rec.Name2 := CopyStr(GetLastErrorText(), 1, MaxStrLen(Rec."Name 2"));
Rec.Modify();
end;
until Rec.Next() = 0;
A Practical Pattern: Batch Processing with Error Logging
Erik walks through a realistic use case — looping through records, processing each one, and logging errors back to the record when processing fails:
- Loop through records (e.g., customers, journal lines, documents)
- Perform your preparatory work on each record
- Call
Commit()to clear the write transaction - Call the processing codeunit wrapped in
IF NOT Codeunit.Run() THEN - If it fails, write the error text back to the record (or a log table)
- If it succeeds, mark it as posted or processed
One important detail: since GetLastErrorText() can return a very long string (potentially a full screen of text), always use CopyStr to truncate it to the field’s maximum length:
Rec."Name 2" := CopyStr(GetLastErrorText(), 1, MaxStrLen(Rec."Name 2"));
This prevents a runtime error from the error text itself being too long for the target field.
Bonus: The Source Code HttpClient Example
The source code in the extension also includes a nice example of using the same IF pattern with HttpClient.Get, which similarly returns a boolean indicating success or failure:
pageextension 50106 CustomerListExt extends "Customer List"
{
trigger OnOpenPage()
var
Client: HttpClient;
Response: HttpResponseMessage;
begin
if Client.Get('krjfvndkfjghszjfkgbdzkvjzsdfhbv', Response) then begin
if Response.IsSuccessStatusCode() then
message('All good! %1', Response.HttpStatusCode());
end else
message('%1', GetLastErrorText());
end;
}
This demonstrates that the pattern of checking a boolean return value and using GetLastErrorText() in the failure branch applies broadly across AL — not just to Codeunit.Run.
Summary
The oldest trick in the book for catching errors in AL is simple: wrap Codeunit.Run in an IF statement. When you do this, any error that occurs within the codeunit’s execution scope is caught, and instead of halting execution, the call returns false. You can then use GetLastErrorText(), GetLastErrorCode(), and GetLastErrorCallStack() to inspect what went wrong.
Key things to remember:
- This technique has been available since the early 1990s in Navision/NAV/Business Central
- You cannot have an open write transaction when using this pattern — call
Commit()first - Business Central does not support nested transactions
- Always use
CopyStrwhen storing error text in a field to avoid overflow errors - While modern alternatives like try functions and collectible errors exist, this pattern remains a fundamental tool in every AL developer’s toolkit