Skip to main content
New tool CRON Expression Builder — preview next run times before you schedule Apex. Open the builder →
Debugging stale SOQL return values after a successful Salesforce UI field update.
Apex

SOQL Returns Old Value After UI Update

A Salesforce UI edit looks committed, and then an Apex SOQL query reads the old value back. The mismatch between the UI state and the record Apex retrieves usually comes down to transaction context or caching, and it needs systematic debugging.

The short answer

Salesforce enforces transaction isolation, so a SOQL query running inside the same transaction that made the change can read the pre-update value back. In a trigger, read from Trigger.new or Trigger.newMap rather than re-querying. In a component, re-fetch once the save confirms instead.

Key takeaways SOQL queries within the same transaction that made the initial change may return stale data due to isolation levels. UI updates commit data, but an immediate Apex read might not see the finalized state until the transaction fully unwinds or a new transaction begins. For Apex triggers, always rely on the Trigger.new map to access values successfully committed by the save operation that fired the trigger. If you are running imperative Apex after a UI save, re-execute the component's data fetch, or make the component wait for server confirmation before attempting its next action.

SOQL returns old value after UI update: diagnosis for developers

A field update made through the Salesforce User Interface (UI) shows up in the client view right away, and then a follow-up SOQL query in Apex returns the old, stale value. The behavior is counter-intuitive, but it usually comes from transaction scope, database caching, or the timing of the commit, especially in complex execution contexts.

The symptom: UI vs. Apex data mismatch

Consider the scenario described:

  1. Action: a user modifies a custom field (e.g., Date_Awarded__c) on an Opportunity record in the Salesforce UI.
  2. UI observation: the UI displays the new value immediately, and the LastModifiedDate field updates correctly.
  3. Apex observation: a SOQL query for the same record, run immediately after, returns the pre-update value for Date_Awarded__c, while LastModifiedDate reflects the update.
-- UI shows: Date_Awarded__c = 2026-XX-XX

SELECT Id, Date_Awarded__c, LastModifiedDate
FROM Opportunity
WHERE Id = :recordId
-- Apex SOQL returns: Date_Awarded__c = (Old Value), LastModifiedDate = (New Timestamp)

Root cause analysis: transaction context and commit timing

When data is modified directly through the UI, the change is usually committed to the database as part of the standard save operation the client-side framework initiates (the Aura or LWC rendering process). What decides whether Apex sees it is the context the Apex code runs in.

1. Implicit transaction context

If the Apex code running the SOQL query fires synchronously as part of the same UI save operation, for example an after update trigger or a method called directly from a component save handler, the record might not yet be visible to later queries within that same transaction scope.

Salesforce enforces transaction isolation. Changes made earlier in a running transaction are not guaranteed to be visible to later queries in that transaction unless the executing Apex context issued those changes itself through DML. When the UI saves the record, the running context knows the record ID and its new state, but a standalone SOQL query might still hit a potentially uncommitted or temporarily cached version, or a different snapshot of the database state.

2. Apex execution context

If the Apex runs after the UI save operation has fully completed, say it was triggered by an asynchronous event the save fired, or by a separate later user action, the data should generally be present. If it is still stale, consider:

  • Custom caching mechanisms: any Apex code or external caching layer in the path might be serving stale data, though that is unlikely for an immediate UI-to-Apex call.
  • Asynchronous queuing: if the UI action triggered an asynchronous process such as a Queueable or future method that runs later, the initial execution context might complete before the asynchronous operation finishes saving.

Developer solutions and workarounds

The goal is to force Apex to read the most recently committed state, getting past any transaction-level visibility issue.

1. Utilizing refresh() or re-querying in the UI layer

If this happens during the LWC or Aura lifecycle immediately after a save, make the component explicitly refresh its wire adapters or imperative calls to re-fetch the record from the server once the save completes. That is a client-side fix for a client-side perception problem, and it leaves the Apex alone.

2. Forcing a database read (Apex solution)

If your Apex logic has to run immediately after the UI save and read the committed value, the most reliable approach is usually to skip the standard SOQL query and read the value from the record variable the DML produced, or to reread the record explicitly with Database.query or SObject.refresh() where the context supports it.

In an Apex Trigger context (after update), the record passed into the trigger context variables (Trigger.newMap) already contains the committed database values, or the values as of the end of the transaction. Reading the field there is preferred over a SOQL query against the same record ID within the same transaction scope.

Reading from trigger context, the recommended approach in triggers:

// Inside an after update trigger
for (Opportunity opp : Trigger.new) {
    // opp.Date_Awarded__c will reflect the value saved by the UI or previous DML
    System.debug('Trigger new value: ' + opp.Date_Awarded__c);
}

Re-loading the record explicitly, where your execution context allows it, can force a fresh database pull. It does not apply universally to every standard scenario:

Opportunity o = [SELECT Id, Date_Awarded__c FROM Opportunity WHERE Id = :recordId FOR UPDATE];
// If the UI update happened outside this immediate transaction, this should now read correctly.

The safest bet: if the UI update is the source of the commit and you are reading it back in Apex that runs immediately after, such as an after insert/update trigger, rely on the data in Trigger.new or the DML result variable rather than a new SOQL query.

Originally reported by reddit.com

Newsletter

One email every Tuesday

New guides, tool updates, and the release-note changes that break things.

No spam. Unsubscribe in one click.

Comments

Loading comments...

Leave a Comment