DYNEXAL TECHNICAL GUIDE • AL DEVELOPMENT

Business Central AL Error Handling: ErrorInfo, TryFunction & Production Patterns

Article #41 · Published: October 3, 2026 · Error Handling · 12–15 min read

Error handling is more than writing Error('Something went wrong.'). In a production Business Central extension, an error should help the user understand what failed, help the developer diagnose the problem, and—when appropriate—offer a safe way to continue or fix it.

Modern AL approach: use ordinary errors for conditions that should stop an operation, ErrorInfo when you need richer and potentially actionable error information, and TryFunction when you deliberately want to catch an error and handle the failure path.

1. The three questions every error should answer

Microsoft's AL documentation treats error handling as a distinct development area, alongside debugging and diagnostics.

2. Basic Error() versus structured error information

The traditional approach is useful when the operation simply cannot continue:

if Customer."No." = '' then
    Error('Customer number is required.');

This is simple, but complex applications often need more context than a single message can provide.

3. ErrorInfo: richer error context

ErrorInfo represents structured information about an error. It can be used to provide a message and additional information for the error experience. Microsoft also documents properties such as page navigation and custom dimensions for error-related actions and handling.

var
    MyErrorInfo: ErrorInfo;
begin
    MyErrorInfo.Message('Customer setup is incomplete.');
    MyErrorInfo.DetailedMessage(
        'Configure the customer posting setup before posting the document.');

    Error(MyErrorInfo);
end;

The exact properties available depend on the target Business Central runtime, so compile the pattern against the symbols for the version you support.

4. Why detailed errors should not expose secrets

Detailed diagnostics are useful, but they should never become a place to dump access tokens, passwords, authorization headers or other sensitive values.

// ❌ Never expose credentials
MyErrorInfo.DetailedMessage(
    'API failed. Token=' + AccessToken);

// ✅ Safer
MyErrorInfo.DetailedMessage(
    'API authentication failed. Check the integration credential.');

For integration failures, a safe HTTP status, endpoint name, correlation identifier or provider error code is generally more useful than the credential itself.

5. TryFunction: when you intentionally catch an error

A method marked with [TryFunction] can return a Boolean result. When the return value is used, an error inside the try method is caught and the call evaluates to false. Microsoft documents this behavior and also notes important transaction considerations.

[TryFunction]
local procedure TryValidateCustomer(CustomerNo: Code[20])
var
    Customer: Record Customer;
begin
    Customer.Get(CustomerNo);

    if Customer.Blocked <> Customer.Blocked::" " then
        Error('Customer is blocked.');
end;

procedure ValidateCustomer(CustomerNo: Code[20])
begin
    if TryValidateCustomer(CustomerNo) then
        exit;

    Error('Customer validation failed.');
end;

6. A critical TryFunction transaction point

Do not treat a TryFunction as a transaction rollback mechanism. Microsoft states that database changes made by a try method aren't rolled back. For on-premises environments, write transactions inside try methods are restricted by default and can produce a runtime error unless the relevant server configuration is changed.

Interview point: “TryFunction catches an error” and “TryFunction rolls back all database changes” are not equivalent statements. Be precise about transaction behavior.

7. GetLastErrorText after a failed try call

When a try method fails, GetLastErrorText() can be used to retrieve the Business Central error text. Microsoft also documents GetLastErrorObject() for inspecting exception details in relevant scenarios.

if not TryCallExternalService() then begin
    ErrorMessage := GetLastErrorText();

    // Log safe diagnostic context if required.
    Error(
        'The external service could not be reached. %1',
        ErrorMessage);
end;

Before exposing the returned text directly to users, review whether it contains implementation details or sensitive information.

8. TryFunction is not a replacement for validation

Do not use TryFunction for every business rule. If a condition can be checked clearly before executing the operation, explicit validation is often easier to read and maintain.

if SalesHeader."Sell-to Customer No." = '' then
    Error('Sell-to Customer No. must be specified.');

if SalesHeader."Posting Date" = 0D then
    Error('Posting Date must be specified.');

Use a try pattern when you genuinely need to handle an operation that may throw an error, especially when the failure path has a meaningful alternative.

9. Actionable errors

A richer error experience can include an action that takes the user to the relevant setup or page. Microsoft documents PageNo and navigation actions on ErrorInfo for this type of scenario.

var
    SetupError: ErrorInfo;
begin
    SetupError.Message('Integration setup is incomplete.');
    SetupError.DetailedMessage(
        'Configure the API endpoint and credential before retrying.');

    SetupError.PageNo(Page::"My Integration Setup");
    SetupError.AddNavigationAction('Open Integration Setup');

    Error(SetupError);
end;

This pattern turns an error from a dead end into a guided correction path.

10. CustomDimensions for machine-readable context

ErrorInfo.CustomDimensions can carry additional key/value information associated with the error. Microsoft documents it as a way to transfer information between the code showing the error and code that handles collected errors or fix actions.

var
    Dimensions: Dictionary of [Text, Text];
begin
    Dimensions.Add('Integration', 'Shopify');
    Dimensions.Add('Operation', 'CreateOrder');

    MyErrorInfo.CustomDimensions(Dimensions);
end;

Keep custom dimensions safe. Do not put credentials, tokens or personal data into diagnostic fields without a clear and appropriate reason.

11. Multiple errors and validation scenarios

Some business processes have several independent validation problems. Instead of failing at the first problem, a design may collect multiple errors and present them together. This is especially useful for document validation where the user can fix several fields in one pass.

When implementing collected-error patterns, keep each message specific and actionable. The user should be able to identify the record or field that needs attention without reading internal implementation details.

12. Error handling in API integrations

API integrations have more than one failure layer:

Handle these cases separately. A generic “API failed” message makes production support much harder.

13. Background jobs need a different error mindset

A Job Queue process has no user sitting at the page waiting to read a message. Errors should therefore be diagnosable from logs/telemetry and should leave the job in a state that operations staff can investigate.

For a background integration, consider recording safe identifiers such as operation type, external document number, HTTP status and retry state. Avoid repeatedly retrying permanent failures such as invalid credentials or malformed business data.

14. Debugging: don't guess from the message alone

When a production error is difficult to reproduce, use the available Business Central troubleshooting tools. Microsoft recommends profiling/debugging in appropriate environments and using telemetry to investigate issues after they occur.

15. Error messages should be written for the user

Compare these two messages:

// Weak
Error('Error 500');

// Better
Error(
    'Shopify order synchronization failed. ' +
    'The external service returned HTTP 500. Try again later.');

The second message tells the user what operation failed and gives a reasonable next step without exposing internal code.

16. A production-ready pattern

procedure SyncOrder(OrderNo: Code[20])
begin
    ValidateOrder(OrderNo);

    if not TrySendOrder(OrderNo) then begin
        LogSafeFailure(OrderNo, GetLastErrorText());
        Error(
            'Order %1 could not be synchronized. ' +
            'Check the integration status and try again.',
            OrderNo);
    end;

    MarkOrderAsSynchronized(OrderNo);
end;

The important architectural idea is separation: validation, external operation, safe diagnostics and business-state updates should not become one large procedure.

17. Common mistakes

18. Interview-ready questions

  1. What is the difference between Error() and ErrorInfo?
  2. How does TryFunction work in AL?
  3. Does TryFunction roll back database changes?
  4. When would you use GetLastErrorText()?
  5. What is an actionable error?
  6. Why would you use CustomDimensions with ErrorInfo?
  7. How would you handle errors in a Job Queue integration?
  8. How do you prevent secrets from appearing in error messages?

19. Practical production checklist

Conclusion

Good AL error handling is part of application design, not just defensive coding. The strongest implementations make failures understandable to users, diagnosable for developers and safe for production operations. ErrorInfo, actionable errors and carefully used TryFunction patterns give AL developers more control, while debugging and telemetry complete the operational side.

Official Microsoft references

← Back to Dynexal Insights