In this video, I tackle one of the oldest “bugs” you can get from AL, a bug that has surfaced for three decades to confuse users. Check it out:

In this video, Erik dives into what he calls the most famous Business Central AL bug — an error that has plagued developers and users alike for close to 30 years. The error message has taken many forms over the decades, but the underlying cause remains the same: a record modification conflict caused by passing record variables by value instead of by reference.
The Error Message
The error message in its current incarnation reads:
“The changes to the Customer record cannot be saved because some information on the page is not up to date. Close the page, reopen it, and try again.”
When users see this, they do exactly what it says — close the page, reopen it, and try again. Sometimes it works, sometimes it doesn’t. Over the years, the message has changed multiple times. For a long time it read something like “the definition of this record has changed since it was last retrieved from the database” — a more technical but arguably more accurate description of what’s actually happening.
Reproducing the Bug
Erik demonstrates the issue with a simple scenario: a page action that calls a “fancy function,” passes a record variable (Rec), and then tries to modify the record afterward. When you run the action, the error fires immediately.
Using the debugger, Erik shows that:
- The break occurs on the
Modifycall after the fancy function returns - If you remove the fancy function call, the modify works perfectly
- The debugger points you to the wrong line — it shows the second modify as the problem, not the one buried inside the function call
The Root Cause: Pass by Value vs. Pass by Reference
The fancy function accepts a Customer record parameter and modifies it internally. The critical detail is that the record is passed by value (without the var keyword), which means the function receives a copy of the record.
Here’s what happens step by step:
- The page action calls the fancy function, passing
Recby value - Inside the function, the copy of the record is modified and saved to the database via
Modify - Control returns to the calling code, but
Recstill contains the original values (because only a copy was modified) - When the calling code tries to modify
Recagain, Business Central detects that the record in the database no longer matches whatRecthinks is there - The error is thrown
This is where AL’s behavior can be truly confusing. Erik points out that whether you need to use var depends on the underlying data type. Older data types (like Record) are passed as copies by default, while newer types (like Dictionary, JsonObject, etc.) are actually .NET objects behind the scenes and behave as references even without var. There’s no single consistent rule — you have to know which types behave which way.
The Fix: Use VAR or Re-read the Record
Option 1: Pass by Reference
The simplest fix is to add the var keyword to the parameter declaration. When you pass the record by reference, both the calling code and the function operate on the same record instance. Any modifications inside the function are immediately reflected in the caller, so there’s no conflict on the subsequent modify.
Option 2: Re-read the Record
Sometimes you can’t control the code that’s doing the modify deep in the call stack — it might be standard code or a third-party extension. In those cases, you can re-read the record after the function returns:
Rec.Find('=');
The Find('=') call is functionally equivalent to a Get using the current primary key values already stored in the record. You could also write it as:
Rec.Get(Rec."No.");
Both approaches refresh Rec with the current database values, so the subsequent modify won’t conflict.
Why This Bug Is So Hard to Track Down
The real difficulty with this bug is that:
- The debugger shows you where the error occurs (the second modify), not where the bug actually is (the first modify inside a function that received a copy)
- In real-world systems, the call stack can be very deep — you might be calling something that calls something that calls something that does a modify, and then back up the call tree something else triggers another modify
- The behavior can be intermittent — it depends on which code paths are triggered, which validations fire, and what data conditions exist
Erik mentions he has spent hours tracking down instances of this bug in large systems with many moving pieces, where the root cause is buried several layers deep in the call stack.
Bonus: Another Pesky Bug
The source code included with this video also showcases another classic AL bug — a division by zero hiding in plain sight:
tableextension 50143 "Sales Lines" extends "Sales Line"
{
fields
{
modify(Quantity)
{
trigger OnBeforeValidate()
begin
Call(Quantity);
end;
}
}
procedure Call(q: Decimal)
begin
Call2(q, 10);
end;
local procedure Call2(Q: Decimal; P: Decimal)
begin
call3(q, p, -10);
end;
local procedure call3(q: Decimal; p: Decimal; u: Integer)
begin
if q > q / (p + u) then
message('Hello my little bug!');
end;
}
Notice what happens in call3: the parameters p and u are 10 and -10 respectively, so p + u equals 0. This means q / (p + u) is a division by zero. The bug is obscured by spreading the values across multiple procedure calls — a pattern that’s all too common in real codebases where the individual pieces look reasonable but the combination is fatal.
Summary
The “record has changed” error is arguably the most well-known bug pattern in Business Central AL development. It stems from a fundamental misunderstanding — or oversight — about how record variables are passed between procedures. The key takeaways are:
- Always consider using
varwhen passing record variables to procedures that might modify them - Re-read the record with
Find('=')orGetwhen you can’t control the downstream code - Remember that the debugger lies — it shows you the symptom, not the cause
- Be aware of AL’s inconsistent pass-by-value behavior — older types like Record are copied, while newer .NET-backed types like Dictionary and JSON types are passed by reference even without
var
This bug has been around for nearly 30 years under various error messages, and it will likely continue to catch developers for years to come. Understanding the mechanics behind it is the best defense.