I wrote some code on Opportunity that compares new values to OldMap values and should execute a record clone only if the record was JUST Closed Lost. I'm not sure if my interpretation of OldMap is wrong, or if there is something else in my code explaining why it would be running this code twice during recursion scenarios, but the end result is that I am getting 2 new opportunity clones instead of one.

Question: Does oldMap maintain the original record values, even throughout multiple loops through the order of execution due to recursion? Or should the oldMap values change as the record goes through the loop multiple times? I know I can solve recursion with an ID set or map to check if the record has already run through the code, but I thought that oldMap comparisons would serve a similar purpose if written correctly.

I searched and could not find a similar question, and the Trigger class documentation doesn't explicitly define if/when oldMap changes:


A map of IDs to the old versions of the sObject records.

This map is only available in update and delete triggers.

Code for Reference:

protected override void beforeUpdate(){
    system.debug('Start MasterOpportunityTrigHdlr IsUpdate - Before');
    Opportunity oldOpp;
    String stage, stageReason;
    String oldStage, oldStageReason;
    Boolean newClose;
    Boolean transferred;
    Opportunity clonedOpp;
    Id originalOwner;
    Id newOwner;
    Date assignedDate;
    List<Opportunity> OppsToInsert = new List<Opportunity>();

    For(Opportunity opp : newList){
        stage = opp.StageName;
        stageReason = opp.Closed_Lost_Options__c;
        oldOpp = oldMap.get(opp.Id);
        oldStage = oldOpp.StageName;
        oldStageReason = oldOpp.Closed_Lost_Options__c;
        assignedDate = System.today();
        originalOwner = opp.OwnerId;
        newClose = false;
        transferred = false;
        if(CLOSED_LOST.equalsIgnoreCase(stage) && !CLOSED_LOST.equalsIgnoreCase(oldStage)){
            newClose = true;
                transferred = true;
                newOwner = opp.Transfer_To__c;

        if(newClose && transferred){
            clonedOpp = OpportunityUtil.cloneOpportunity(oldOpp);
            clonedOpp.OwnerId = newOwner;
            clonedOpp.Last_Assigned_Date__c = assignedDate;
            clonedOpp.Transferred_From__c = originalOwner;


    system.debug('End MasterOpportunityTrigHdlr IsUpdate - Before');

1 Answer 1


In The documentation of Trigger Order of Execution (link)

we can see in the bottom

Trigger.old contains a version of the objects before the specific update that fired the trigger. However, there is an exception. When a record is updated and subsequently triggers a workflow rule field update, Trigger.old in the last update trigger won’t contain the version of the object immediately prior to the workflow update, but the object before the initial update was made. For example, suppose an existing record has a number field with an initial value of 1. A user updates this field to 10, and a workflow rule field update fires and increments it to 11. In the update trigger that fires after the workflow field update, the field value of the object obtained from Trigger.old is the original value of 1, rather than 10, as would typically be the case.

Based on that, I think Trigger.old will maintain the original value even throughout multiple loops through the order of execution due to recursion

  • I am familiar with the Order of Execution, I already know that my code is firing more than once, as I understand that workflow rules etc can cause step 12 to fire before and after update triggers again - my question was not "why is the Trigger Order of Execution Running Twice" it was "Should oldMap remain static even if the record is run through the order of execution twice" Mar 7, 2018 at 14:42
  • Example: I changed the record from "Qualified" to "Closed Lost", it runs through my code and clones the record. Recursion occurs, and the record runs through before/after update triggers again... but the record was Closed Lost in the first round - should the oldMap still say "Qualified" or should the oldMap now say "Closed Lost" on the second loop? Mar 7, 2018 at 14:47
  • Sorry I did not get your Question, I will say that it will be "Qualified" as we can see in the documentation
    – Akram G
    Mar 7, 2018 at 15:13
  • Where can we see that in the documentation? Documentation says it will maintain the old record, not the original record. Do we assume that old === original? If so I'm fine with that, just trying to get clarity on the expected values of oldMap Mar 7, 2018 at 15:15
  • I saw it in the bottom of the link in my answer
    – Akram G
    Mar 7, 2018 at 15:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .