# Business Central: "Another user has modified the record after you retrieved it"

> Source: https://netray.co/erp-help/bc-another-user-has-modified-the-record-after-you-retrieved-it
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: ERP Help

## Short answer

Business Central uses optimistic concurrency. Every record carries a row version, and a write is rejected if the stored version no longer matches what you read. For users, refresh the page and redo the edit. For developers, read and modify in the same short transaction, use LockTable or the right read isolation, and send an If-Match ETag from API clients.

## Kind

error

## System

dynamics

## Erp

Microsoft Dynamics 365 Business Central

## Module

AL Development / Concurrency

## Query

Another user has modified the record after you retrieved it from the database Business Central

## Alternate Queries

- business central record was changed by another user error
- business central AL LockTable concurrency modify error
- business central if-match etag 412 precondition failed

## Applies To

Business Central online and on-premises, AL extensions and web service clients

## Steps

### How to resolve it

**Items:**

- As a user, refresh the page (or close and reopen the card) so you get the current record, then re-enter your change and save.
- Ask who else is editing. Open the Sessions list (Search: Sessions, or the Users page) and check for background sessions such as job queue entries working on the same record.
- Check Job Queue Entries for jobs touching the same table at the same time as users, and move heavy jobs to an off-hours schedule.
- As a developer, find the code that reads and later writes the record. Make sure the Get and Modify happen in the same transaction without a Commit or user interaction in between.
- Call Rec.LockTable() before reading when you intend to modify, so other sessions wait instead of racing you. Keep the locked window short.
- For API and OData clients, fetch the record, keep its ETag, and send it in the If-Match header on PATCH. Handle a 412 response by re-reading and retrying.
- Avoid modifying the same record from event subscribers that fire during a long posting routine, as these cause the conflict for the page that triggered the post.
- Log the table and primary key in your error handler so repeated conflicts can be traced to one hot record.

## Sections

### How optimistic concurrency works here

**Body:**

Business Central does not lock a record when you open a card. Each table row has a hidden SystemModifiedAt value and an underlying SQL row version. When a page saves, the runtime checks that the version of the row in the database equals the version it read earlier. If another session has written the row in between, the check fails and you see the message.

This is intentional. It prevents a user with a stale screen from silently overwriting someone else's change. The cost is that on busy records, such as a sales header being posted while someone edits it, conflicts are normal and should be handled rather than prevented.

In the web client the fix is a refresh. The harder cases come from automation: job queue entries, web services and event subscribers that touch the same rows as interactive users.

### Fixing it in AL

**Body:**

The common AL defect is reading a record, doing slow work (a web call, a dialog, a Commit), and then calling Modify. Between the read and the write another session changes the row. Restructure so the read and the write are adjacent. If you must do slow work first, do it before the Get, then Get and Modify in a short block.

When you need to guarantee exclusivity for a read-modify-write, call LockTable on the record variable before the Get. In newer runtime versions you can also set the ReadIsolation property on the record to UpdLock. Both make the read take an update lock so competing writers wait. Keep the lock window short, because long locks produce timeouts and deadlocks for everyone else.

Never swallow the error with an empty if-not-then. If the modification fails, either retry once after re-reading the record or surface the conflict to the caller. Silent failure creates data that looks saved and is not.

**Code:**

procedure BumpPriority(ItemNo: Code[20])
var
    Item: Record Item;
begin
    Item.LockTable();
    if Item.Get(ItemNo) then begin
        Item.Description := CopyStr('Updated ' + Item.Description, 1, MaxStrLen(Item.Description));
        Item.Modify(true);
    end;
end;

### API clients and integrations

**Body:**

The standard APIs return an ETag with each record (the @odata.etag value). A PATCH or DELETE must send it in the If-Match header. If someone changed the record after you read it, Business Central answers 412 Precondition Failed. That is the same concurrency check, expressed over HTTP.

The correct client pattern is: GET the record, apply the change, PATCH with If-Match, and on 412 re-GET and reapply. Sending If-Match with an asterisk bypasses the check and can overwrite another user's change, so use it only for data you own exclusively.

Integrations that update thousands of records in parallel often cause their own conflicts when two threads patch the same parent, such as a sales header and its lines. Serialise per parent document rather than adding retries blindly.

**Code:**

- PATCH /api/v2.0/companies({companyId})/customers({customerId})
If-Match: W/"JzE5OzMwNTc4MzQ5..."
Content-Type: application/json

{ "phoneNumber": "555-0100" }

## Pitfalls

- Holding a record variable across a Commit or a dialog and then modifying it.
- Using LockTable broadly or for long operations, which turns conflicts into timeouts and deadlocks.
- Sending If-Match with an asterisk to make 412 errors go away, which overwrites other users' changes.
- Running heavy job queue entries on the same tables during business hours.
- Catching and ignoring the error so a failed save looks like success.

## Ai Angle

Diagnosing which session or job collides with users is a log-reading task. A grounded assistant can correlate the failing table and key with job queue entries, change log rows and session activity, and tell you which scheduled job and which user overlapped. ERPray works read-only on that data and shows the query. Fixing the code or the schedule is still a developer's change, and a model should not auto-retry writes.

## Faq

### Why does Business Central say another user modified the record?

**Answer:** Business Central uses optimistic concurrency. It remembers the row version when you read a record and rejects the save if the stored version has changed since. Another user, a job queue entry or an event subscriber wrote the row first. Refresh the page and re-apply your edit.

### Does LockTable fix this error in AL?

**Answer:** Often, when used correctly. Calling LockTable before the Get makes the read take an update lock so competing writers wait. It only works if the read and Modify happen in the same short transaction. Long locks cause timeouts, so keep the locked section small.

### What does HTTP 412 mean from the Business Central API?

**Answer:** It is the same concurrency check over HTTP. You sent an If-Match ETag that no longer matches the record, because someone changed it after your GET. Read the record again, reapply your change, and send the new ETag. Do not bypass it unless you own the data.

### Can a job queue entry cause this for users?

**Answer:** Yes. A job that updates the same table or record that users edit, such as sales documents or items, can change the row between a user's read and save. Check Job Queue Entries for overlapping schedules and move heavy jobs outside working hours where possible.

## Search terms

another user has modified the record business central, business central optimistic concurrency, al locktable business central, business central api if-match etag, business central 412 precondition failed, business central job queue conflict, bc read isolation updlock, business central record changed by another user

---
Talk to an engineer: https://netray.co/contact?intent=erp_ai | Machine catalog: https://netray.co/agent/catalog.json
