Business Central AL Error Handling: Error, TryFunction & Logging
Errors are unavoidable in Business Central development, especially when you work with posting routines, APIs, external services, permissions and data validation. Good AL error handling should do more than stop execution: it should give users a clear next step, preserve useful diagnostic information and make production problems easier to investigate. In this practical guide, you will learn how to use Error, ErrorInfo, TryFunction, Codeunit.Run, GetLastErrorText, GetLastErrorCallStack and telemetry in maintainable AL solutions.
What is error handling in Business Central AL?
Error handling is the way an AL application detects an invalid or failed operation and decides what should happen next. A validation error may need to stop the current process and tell the user how to fix the data. An integration failure may need to capture the response, log diagnostic details and allow a background process to continue with other work.
Business Central provides several approaches, including the Error method, ErrorInfo, TryFunction, the Boolean return value of Codeunit.Run, last-error methods and telemetry. Microsoft also recommends clear, actionable error messages because good messages reduce user friction and make troubleshooting easier. Microsoft Learn: AL error handling
1. The Error method
The simplest way to stop execution and report a problem is the Error method. When it is raised, execution of the current AL flow stops and Business Central displays the error.
procedure ValidateQuantity(Quantity: Decimal)
begin
if Quantity <= 0 then
Error('Quantity must be greater than zero.');
end;
For new production code, prefer labels rather than hard-coded user-facing strings so messages can be translated and maintained.
var
QuantityErr: Label 'Quantity must be greater than zero.';
procedure ValidateQuantity(Quantity: Decimal)
begin
if Quantity <= 0 then
Error(QuantityErr);
end;
Error when the current operation cannot safely continue. Do not use errors as normal control flow for expected success/failure decisions when a Boolean result or another explicit design would be clearer.2. ErrorInfo: better error messages
ErrorInfo provides a structured way to describe an error. It can carry information such as a title, message, detailed message, error type, data classification and actionable navigation or fix actions. This allows an error to be more useful than a plain text string.
procedure ValidateCustomer(CustomerNo: Code[20])
var
CustomerErr: ErrorInfo;
begin
if CustomerNo = '' then begin
CustomerErr.Title('Customer is required');
CustomerErr.Message('Select a customer before continuing.');
CustomerErr.DetailedMessage('The current operation requires a valid Customer No.');
Error(CustomerErr);
end;
end;
Business Central supports passing an ErrorInfo instance to Error or Dialog.Error. Microsoft documents ErrorInfo as a structure for grouping information about an error and supporting richer error experiences. ErrorInfo and Dialog.Error
When should you use ErrorInfo?
- When the user needs a clear title and actionable explanation.
- When you need a detailed diagnostic message in addition to the short message.
- When the error should include navigation or fix-it actions.
- When the error needs additional metadata or custom dimensions.
3. TryFunction: catching AL errors
A method marked with the TryFunction attribute becomes a try method. When called as a Boolean expression, it returns true if the operation succeeds and false if an error occurs. The error is caught instead of immediately being shown to the user.
codeunit 50130 "Dynexal Try Function Demo"
{
trigger OnRun()
begin
if TryValidateCustomer() then
Message('Validation succeeded.')
else
Message('Validation failed.');
end;
[TryFunction]
local procedure TryValidateCustomer()
begin
Error('Customer validation failed.');
end;
}
Microsoft notes that the Boolean return value must actually be used for the call to behave as a try call. Simply calling a TryFunction method without consuming its return value does not make it a caught-error call. Handling errors using try methods
4. Important TryFunction transaction behavior
TryFunction is often misunderstood as an automatic database rollback mechanism. You should not treat it that way. Current Microsoft documentation states that database changes made by a try method are not rolled back. For Business Central online, write transactions can be present in try methods, while on-premises behavior is controlled by the Business Central Server configuration. Because of this, database writes inside try methods require careful design.
TryFunction means “all changes will be undone if something fails.” If a process needs reliable transaction boundaries, design those boundaries explicitly and understand where commits and writes occur.For most integration scenarios, a good pattern is to keep the try method focused on an operation whose success or failure you want to detect, capture the error information, and then handle the result outside the try method.
5. GetLastErrorText()
When a caught error occurs, GetLastErrorText can return the last error message. It is particularly useful after a failed TryFunction or Codeunit.Run call.
ClearLastError();
if not TrySendRequest() then begin
ErrorText := GetLastErrorText(true);
Message('The operation failed: %1', ErrorText);
end;
The Boolean parameter can be used to exclude customer content from the returned error text. Microsoft documents this parameter as a way to exclude sensitive data such as primary key values. GetLastErrorText
6. GetLastErrorCallStack()
GetLastErrorCallStack returns the call stack associated with the last error. It is useful when you need to understand which procedures led to a failure.
ClearLastError();
if not TryProcessOrder() then begin
ErrorText := GetLastErrorText(true);
ErrorCallStack := GetLastErrorCallStack();
end;
For some error types, the returned call stack may not contain every call. Microsoft recommends using the debugger when you need the complete call stack for certain errors. GetLastErrorCallStack
7. Codeunit.Run() with a Boolean result
Another established pattern is to execute a codeunit and inspect its Boolean return value.
ClearLastError();
if not Codeunit.Run(Codeunit::"Dynexal Order Processor") then begin
ErrorText := GetLastErrorText(true);
Message('Order processing failed: %1', ErrorText);
end;
If the Boolean result is omitted and the codeunit fails, the error is raised normally. If the result is used, you can handle the failure in code. Microsoft also documents important transaction behavior for Codeunit.Run: when the Boolean return value is used, the transaction contained in the codeunit is always committed. Design this pattern carefully when database writes are involved. Codeunit.Run
8. Error handling for REST APIs and HttpClient
Integration code should distinguish between an HTTP request failing at the transport level and the remote API returning an unsuccessful HTTP status code. A useful pattern is to check HttpClient.Get or Send first, then inspect the response status and content.
procedure CallExternalApi(Url: Text): Boolean
var
Client: HttpClient;
Response: HttpResponseMessage;
ResponseText: Text;
begin
if not Client.Get(Url, Response) then begin
Error('The external service could not be reached.');
end;
if not Response.IsSuccessStatusCode() then begin
Response.Content().ReadAs(ResponseText);
Error('External API returned status %1. Response: %2',
Response.HttpStatusCode(), ResponseText);
end;
exit(true);
end;
For a deeper look at HTTP requests, headers, content and responses, see HttpClient in Business Central AL.
9. Handling JSON/API errors
External APIs often return JSON error objects. Do not assume every response has the same schema. Check the HTTP status first and then safely parse the response body.
if not Response.IsSuccessStatusCode() then begin
Response.Content().ReadAs(ResponseText);
// Parse known error properties only when they exist.
Error('The API request failed with HTTP status %1.',
Response.HttpStatusCode());
end;
For practical JSON parsing patterns using JsonObject, JsonArray, JsonToken and JsonValue, see JSON Handling in Business Central AL.
10. Collecting errors instead of stopping at the first one
Some processes need to validate many records and report several problems together instead of stopping at the first error. Business Central provides error collection features for scenarios such as bulk validation. This can be more user-friendly than raising one error, fixing it, rerunning the process and discovering the next problem.
Choose error collection when the business process benefits from a consolidated validation result. For a single blocking condition, a normal Error or ErrorInfo may be simpler.
11. Logging errors with telemetry
Production applications need more than user-facing messages. Telemetry can help developers understand failures that happened after the user left the page or when a background process failed.
Business Central exposes telemetry capabilities through the system telemetry framework. For example, the Feature Telemetry codeunit provides LogError overloads that can send error text, call-stack information and custom dimensions. Feature Telemetry
if not Success then begin
FeatureTelemetry.LogError(
'0000XYZ',
'Dynexal Integration',
'Customer synchronization',
GetLastErrorText(true),
GetLastErrorCallStack());
end;
Telemetry should contain useful technical context but should not become a dumping ground for sensitive customer information. Use appropriate data classification and avoid logging secrets such as access tokens.
12. A practical error-handling pattern for integrations
A robust integration can separate the operation, result handling and diagnostics.
procedure SyncCustomer(CustomerNo: Code[20]): Boolean
var
ErrorText: Text;
ErrorCallStack: Text;
begin
ClearLastError();
if not TrySyncCustomer(CustomerNo) then begin
ErrorText := GetLastErrorText(true);
ErrorCallStack := GetLastErrorCallStack();
// Log technical details where appropriate.
FeatureTelemetry.LogError(
'0000CUST',
'Dynexal Customer Sync',
'Customer synchronization failed',
ErrorText,
ErrorCallStack);
exit(false);
end;
exit(true);
end;
[TryFunction]
local procedure TrySyncCustomer(CustomerNo: Code[20])
begin
// Call the external service and process the response.
// Raise Error when the operation cannot continue.
end;
The exact architecture depends on whether the process is interactive, scheduled through a job queue, or part of a larger posting transaction. The important principle is to separate user-facing behavior from technical diagnostics.
13. Common error-handling mistakes
Using Message instead of Error
Message does not communicate a blocking failure in the same way as Error. If the process must stop because the data is invalid, use the appropriate error mechanism.
Showing raw technical errors to users
A raw HTTP response, SQL-style technical detail or call stack is usually not an appropriate user message. Give the user a concise explanation and keep technical diagnostics for logs or telemetry.
Assuming TryFunction rolls back everything
This is one of the most important mistakes. Current Microsoft documentation explicitly says database changes made by a try method are not rolled back. Design transaction behavior deliberately.
Calling a TryFunction without using its result
If the Boolean return value is not consumed, the call is not treated as a caught-error try call. Use the result in an if or assignment when you intend to catch the error.
Logging secrets
Never put passwords, OAuth access tokens, API keys or unnecessary personal/customer data into error messages or telemetry.
Ignoring the call stack
For production failures, error text alone may not identify the source. Capture a call stack when it adds useful diagnostic value.
14. Error handling best practices
- Use clear and actionable error messages.
- Use labels for user-facing text.
- Use
ErrorInfowhen a structured error experience is useful. - Use
TryFunctionwhen you genuinely need to catch an AL error and branch on success/failure. - Do not assume TryFunction automatically rolls back database changes.
- Use
ClearLastError()before capturing a new failure when appropriate. - Use
GetLastErrorTextandGetLastErrorCallStackfor diagnostics. - Keep technical details out of normal user-facing messages.
- Use telemetry for production diagnostics and background processes.
- Never log secrets or unnecessary sensitive information.
- Test both successful and failure paths.
- Document integration-specific retry and failure behavior.
15. Error handling checklist for API integrations
- Validate required configuration before making the request.
- Use secure authentication and never hard-code credentials.
- Handle transport/request failures.
- Check HTTP status codes.
- Safely read and parse error responses.
- Do not expose tokens or sensitive response data to users.
- Capture useful diagnostic details.
- Decide whether the operation should retry, skip, queue or stop.
- Log failures in a way that can be investigated later.
- Test timeout, authentication, validation, rate-limit and server-error scenarios.
16. Error handling vs debugging
Error handling decides what the application should do when something goes wrong. Debugging helps the developer discover why it went wrong. They work together but are not the same thing.
During development, use breakpoints and the AL debugger to inspect variables and call stacks. In production, telemetry and structured diagnostics can help investigate failures that cannot be reproduced interactively.
FAQ
Does TryFunction catch every possible error?
Try methods are designed to catch errors and exceptions that occur during AL execution. Their behavior has specific rules, so do not treat them as a universal replacement for all error-handling mechanisms. Review the current TryFunction documentation for your Business Central runtime.
Does TryFunction roll back database changes?
No. Current Microsoft documentation states that changes made by a try method are not rolled back. Transaction behavior should therefore be designed carefully.
When should I use Codeunit.Run?
Use it when a codeunit needs to be executed and you want to inspect its Boolean success/failure result. Be aware of the transaction semantics documented for the Boolean-returning form.
What is the difference between GetLastErrorText and GetLastErrorCallStack?
GetLastErrorText gives the last error message, while GetLastErrorCallStack gives the call stack associated with the last error.
Should I show GetLastErrorText directly to users?
Not automatically. The returned text may contain technical or sensitive details. Prefer a clear user-facing message and keep diagnostic details in appropriate logs or telemetry.