The term Recursion means the repeated occurrence of a task or process. In the context of Salesforce triggers, recursion is when a trigger invokes itself in a loop, leading to repeated execution and infinite iterations.
It is essential to know the causes of trigger recursion so that we can handle it and avoid hitting governor limits, which can result in unexpected errors.
In this Salesforce Apex tutorial, we will learn what a recursive trigger is and how to avoid trigger recursion in Salesforce Apex.
What is a Recursive trigger in Salesforce Apex?
In Salesforce Apex, a recursive trigger is a condition in which a trigger invokes itself repeatedly. This usually happens when the code within the trigger causes an update or action on the same record that initially fired the trigger.
In short, any update or insert of records initiated by the trigger again calls the trigger and continues in a loop. This recursion can lead to excessive system resource consumption, hitting governor limits, and causing data integrity issues.
Types of Recursive Triggers in Salesforce Apex
There are many reasons for recursion in recursive triggers. Now, with the types of recursive triggers, we will understand the different reasons for recursion in Apex triggers.
- Direct Recursion: This trigger recursion occurs when a trigger directly calls itself. It happens when a trigger updates records, and that update invokes the trigger again. This recursion leads to a continuous loop until it hits the governor’s limits.
- Indirect or Cross-Object Recursion: This recursion occurs when the triggers of related records contradict each other while updating a record. For example, an update on a contact record triggers an update on related account records, and a trigger on the account object updates all related contacts, resulting in a recursive loop between contacts and accounts.
- Time-Dependent Recursion: This recursion occurs with time-based workflows and processes that update a record with a delay in time. If there are further trigger annotations that update the records back to the previous state, then it can result in recursive behavior.
- Bulk Data Recursion: In Salesforce, bulk data recursion in triggers occurs when a trigger is fired multiple times during a bulk operation (e.g., inserting or updating multiple records at once). When triggers are not appropriately designed, we might encounter issues like infinite loops or exceeding governor limits.
- Complex Business Logic Recursion: Recursive loops can inadvertently form when complex business logic involves multiple triggers and processes across various objects. These are often the result of interdependent logic, where the outcome of one trigger serves as the input for another, creating an unintentional loop.
Best Practises To Avoid Recursive Trigger in Salesforce
There are some practices to avoid trigger recursion in Salesforce Apex, which are as follows.
Establish Recursive Trigger Guards
To avoid recursion, we can implement a recursive guard mechanism within the trigger logic. This mechanism involves using static variables or custom objects to track whether the trigger has already been executed within the current transaction. Before invoking the trigger’s logic, check the guard variable to determine if the trigger should proceed. By breaking the recursion cycle, we can ensure that the trigger executes only when necessary.
Leverage Trigger Context Variables
Salesforce provides trigger context variables that contain crucial information about the execution context. These variables include Trigger.old, Trigger.new, and Trigger.newMap hold records before and after the trigger event.
Properly utilizing these variables can help you avoid unnecessary trigger invocations and mitigate the risks of recursion. By comparing the old and new record values, you can determine if the trigger needs to perform specific actions, thereby preventing redundant invocations.
Use Static Variables for Data Caching
Recursion can occur in Salesforce when the same logic is executed repeatedly, often due to excessive database queries triggered by a trigger. To avoid this, we can use static variables to store data temporarily. This way, we don’t need to repeatedly fetch the same data, which helps prevent the trigger from running unnecessarily. Storing frequently used data reduces unnecessary database queries and enables the trigger to run more efficiently.
How to Avoid Trigger Recursion in Salesforce Apex
Now, we will discuss the solution methods that can be used to tackle the trigger recursion and avoid it.
1. Use Static Boolean Variable
In this method, we can create a class with a static Boolean variable with a default value of true. In the trigger, before executing your code, keep a check that the variable is true or not. Once you check, make the variable false.
Let’s take an example where we have a trigger for the Account object that updates related Contact records whenever an Account’s Industry field is updated.
Static Boolean Variable in a Helper Class
public class TriggerControl {
public static Boolean isFirstRun = true;
}
Use the Static Variable in the Trigger
trigger AccountTrigger on Account (after update) {
if (TriggerControl.isFirstRun) {
// Set the flag to false to prevent recursion
TriggerControl.isFirstRun = false;
// Logic to update related Contacts
List<Contact> contactsToUpdate = new List<Contact>();
for (Account acc : Trigger.new) {
if (acc.Industry != Trigger.oldMap.get(acc.Id).Industry) {
for (Contact con : [SELECT Id, AccountId FROM Contact WHERE AccountId = :acc.Id]) {
con.Description = 'Updated because Account Industry changed to ' + acc.Industry;
contactsToUpdate.add(con);
}
}
}
if (!contactsToUpdate.isEmpty()) {
update contactsToUpdate;
}
}
}
According to the trigger conditions, the description field in the related contacts of the account will be updated.

2. Use Static Set
Using a Static Set or Static Map is another effective method to prevent recursive triggers in Salesforce Apex.
Let’s take an example, we have a trigger for the Opportunity object that updates the related Account whenever the Stage of an Opportunity is updated. If this action triggers another update to opportunity through automation, it could create a recursive loop.
Static Set in a Helper Class
public class TriggerHelper {
public static Set<Id> processedRecords = new Set<Id>();
}Using the Static Set in the trigger class
trigger OpportunityTrigger on Opportunity (after update) {
List<Account> accountsToUpdate = new List<Account>();
for (Opportunity opp : Trigger.new) {
// Check if the Opportunity's Stage has changed
if (opp.StageName != Trigger.oldMap.get(opp.Id).StageName &&
!TriggerHelper.processedRecords.contains(opp.Id)) {
// Add the Opportunity to the records set
TriggerHelper.processedRecords.add(opp.Id);
Account acc = [SELECT Id, Custom_Status__c FROM Account WHERE Id = :opp.AccountId LIMIT 1];
acc.Custom_Status__c = 'Updated from Opportunity Stage: ' + opp.StageName;
accountsToUpdate.add(acc);
}
}
if (!accountsToUpdate.isEmpty()) {
update accountsToUpdate;
}
}In the above method, TriggerHelper.processedRecords tracks the Opportunity records that are already processed and also ensures that each Opportunity is processed only once in a transaction.
This ensures that even if another update occurs, the same record is not processed again during the same transaction.
3. Use Static Map
In Salesforce Apex triggers, using the static map is the common method to avoid recursion in triggers. This method ensures that the same trigger logic is not repeatedly executed within a single transaction, which could lead to performance issues or exceeding governor limits.
To understand the static map method, let’s take an example where a Contact trigger updates the parent Account whenever a Contact is inserted or updated. If the account update triggers another operation, this could cause a loop without controlling recursion.
trigger ContactTrigger on Contact (after insert, after update) {
ContactTriggerHandler.handleTrigger(Trigger.new);
}Using the static map method in the trigger class
public class ContactTriggerHandler {
private static Map<Id, Boolean> processedContacts = new Map<Id, Boolean>();
public static void handleTrigger(List<Contact> contacts) {
List<Account> accountsToUpdate = new List<Account>();
for (Contact con : contacts) {
// Prevent recursion by checking if the contact is already processed
if (!processedContacts.containsKey(con.Id)) {
processedContacts.put(con.Id, true); // Mark as processed
// Add the related Account to the update list
if (con.AccountId != null) {
accountsToUpdate.add(new Account(Id = con.AccountId, LastModifiedDate = System.now()));
}
}
}
// update account objects
if (!accountsToUpdate.isEmpty()) {
try {
update accountsToUpdate;
} catch (DmlException e) {
System.debug('Error updating Accounts: ' + e.getMessage());
}
}
}
}
In the above method, the static map processedContacts stores the IDs of Contact records already processed in this transaction. Static variables retain their values throughout the execution, ensuring consistency across multiple trigger invocations.
By using the static map, even if the trigger gets called again within the same transaction, the same record won’t be processed again.
This way, we can use the above methods to avoid the recession of triggers in Salesforce Apex.
Conclusion
In Salesforce Apex, it is very crucial to handle trigger recursion to maintain org data and avoid situations like hitting the governor limits. In this Salesforce tutorial, we discussed methods to avoid trigger recursion, such as using a static boolean variable, a static set, and the static map in your trigger class. Using these methods, you can efficiently control the recursion of Apex triggers.
You may also like to read.
- Access Custom Label in Salesforce Apex
- Trigger.New in Salesforce Apex
- Trigger.Old in Salesforce Apex

Abhijeet is a skilled Salesforce developer with experience in developing and integrating dashboards, data reports, and Salesforce applications. He is also skilled at optimizing processes and flow automation processes, coding, and executing complex project architecture. Read more about us | LinkedIn Profile.