function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Chuck M RohllfChuck M Rohllf 

maximum trigger depth exceeded Account trigger event AfterUpdate Contact trigger event AfterUpdate

I have a trigger on Account that will update all related Contacts when updated.  I'm receiving the following error:  maximum trigger depth exceeded Account trigger event AfterUpdate Contact trigger event AfterUpdate  Any guidance would be appreciated.  Here is my trigger:
trigger UpdateRelatedContacts on Account (after update){
   List<Contact> contactList = new List<Contact>();
   List<Contact> contactList1 = new List<Contact>();
for(Account acc: [SELECT Id,RecordTypeID,(SELECT Id,RecordTypeID FROM Contacts) FROM Account WHERE Id in: Trigger.new]){
   If(acc.Contacts.size()>0 && (acc.RecordTypeID =='012E0000000PR3u' || acc.RecordTypeID =='012440000002T4h')){
     contactList.addALL(acc.Contacts);
    }
}
for(contact c:contactList){
    If(c.RecordTypeID !='012440000002T6J'){
       c.checkbox__c=true;
       contactList1.add(c);
}

update contactList1;
Egor Gerasimenko 9Egor Gerasimenko 9
This error is typically due to a circular trigger that creates an infinite loop. You are updating a contact which fires a trigger which update an account which fires a trigger which updates a contact, and so on. Check your Contact AfterUpdate triggers, somewhere in them you are doing an account update. You need to break this infinite loop, but it's hard to say how without seeing the contact trigger code.
Chuck M RohllfChuck M Rohllf
Looks like I have 1 After Update Trigger on Contact that does update Account.  Code attached.  Any thoughts?
rigger CountContacts on Contact (after insert, after update, after delete) {

    Set<Id> AccountIds = new Set<Id>();
    Map<Id,Account> Summary = new Map<Id,Account>();
    
    
    for (Contact c : (Trigger.new<>null ? Trigger.new : Trigger.old)){
        if (c.Sync_with_Marketo__c && c.AccountId <> null){
            AccountIds.add(c.AccountId);
            Summary.put(c.AccountId,new Account(id=c.AccountId,of_Sync_to_Marketo_Contacts__c=0));
        }
    }
    
    if (!AccountIds.isEmpty())
    {
      for (AggregateResult aggr : [select count(id),AccountId FROM Contact WHERE Sync_with_Marketo__c=true AND AccountId IN : AccountIds GROUP BY AccountId])
      {
        Account a = Summary.get((Id)aggr.get('AccountId'));
        a.of_Sync_to_Marketo_Contacts__c = (Decimal)aggr.get('expr0');
        Summary.put(a.Id,a);
      }
      
      update Summary.values();
    }

 
Egor Gerasimenko 9Egor Gerasimenko 9

Well, the easiest way to break this loop would probably be to check whether you actually need to update your contacts and accounts before you update them. For example

a.of_Sync_to_Marketo_Contacts__c = (Decimal)aggr.get('expr0');
Summary.put(a.Id,a);

Is this necessary? What if 'of_Sync_to_Marketo_Contacts__c' already has the right value? If you did a check first then you'd avoid pointlessly firing DML operations that did nothing, and avoid firing triggers as well. Same thing here:

If(c.RecordTypeID !='012440000002T6J'){
       c.checkbox__c=true;
       contactList1.add(c);
}

You could easily check the state of that checkbox and avoid pointless trigger calls

If(c.RecordTypeID !='012440000002T6J' && !c.checkbox__c){
       c.checkbox__c=true;
       contactList1.add(c);
}


Lastly, this code is ... well... not great. Lots of things you could clean up

trigger UpdateRelatedContacts on Account (after update) {
    List<Contact> contacts = [SELECT Id FROM Contacts WHERE 
        checkbox__c = false AND RecordTypeID != '012440000002T6J' AND AccountId in :Trigger.new AND 
        (Account.RecordTypeID =='012E0000000PR3u' OR Account.RecordTypeID =='012440000002T4h')];

    for (Contact cont : contacts {
        c.checkbox__c=true;
    }

    update contacts;
}

Much simpler, a single SOQL call that does all the filtering and gives you back only the results you actually need, then you simply set the one field you care about and update that list. Although having fields like 'checkbox__c' is a terrible idea. Nobody will ever know what that field is for, give it a proper name that describes it at least a little bit.

trigger CountContacts on Contact (after insert, after update, after delete) {

    Set<Id> accountIds = new Set<Id>();
    
    for (Contact c : (Trigger.new<>null ? Trigger.new : Trigger.old)){
        if (c.Sync_with_Marketo__c && c.AccountId <> null){
            accountIds.add(c.AccountId);
        }
    }
    
    if (!accountIds.isEmpty())
    {
        List<Account> accountsToUpdate = new List<Account>();
        List<Account> accounts = [SELECT Id, of_Sync_to_Marketo_Contacts__c, (SELECT Id FROM Contacts WHERE Sync_with_Marketo__c=true) FROM Account WHERE Id IN :accountIds];
        for (Account acc : accounts ) {
            if (acc.of_Sync_to_Marketo_Contacts__c != acc.Contacts.size()) {
                acc.of_Sync_to_Marketo_Contacts__c  = acc.Contacts.size();
                accountsToUpdate.add(acc);
            }
        }
      
        update accountsToUpdate;
    }
}
Although frankly it would be much easier to just create a rollup summary field that counts how many contacts have Sync_with_Marketo__c set to true. It would dynamically update on its own without any triggers. https://help.salesforce.com/articleView?id=000249282&type=1 is almost exactly what you need, except you'd have to add 1 criteria.
Egor Gerasimenko 9Egor Gerasimenko 9
Oh, never mind, Contacts and Accounts aren't in a master-detail relationship so you can't create a rollup summary. So ignore that last bit
Maharajan CMaharajan C
Hi Rhollf,

In your Contact Trigger you are blindly updating the Accounts in after update..

So try to add one more condition in the After Update Trigger:

Trigger CountContacts on Contact (after insert, after update, after delete) {

    Set<Id> AccountIds = new Set<Id>();
    Map<Id,Account> Summary = new Map<Id,Account>();
 
    if(Trigger.isInsert || Trigger.isDelete) 
    {
        for (Contact c : (Trigger.new<>null ? Trigger.new : Trigger.old)){
        if (c.Sync_with_Marketo__c && c.AccountId <> null){
            AccountIds.add(c.AccountId);
            Summary.put(c.AccountId,new Account(id=c.AccountId,of_Sync_to_Marketo_Contacts__c=0));
        }
    }
    }
    else if(Trigger.isUpdate) 
    {
        for (Contact c : Trigger.new){
        if (c.Sync_with_Marketo__c && c.AccountId <> null && c.AccountId != Trigger.oldMap.get(c.Id).AccountId){
            AccountIds.add(c.AccountId);
            Summary.put(c.AccountId,new Account(id=c.AccountId,of_Sync_to_Marketo_Contacts__c=0));
            // The below logic you are completely missed.
            // The below If loop will reduce the 1 contact count from the Old Account whilee you are updating the Contact
            // with New Account.
            if(Trigger.oldMap.get(c.Id).AccountId <> null)
            {
                Id oldAccId = Trigger.oldMap.get(c.Id).AccountId;
                AccountIds.add(oldAccId);
                Summary.put(oldAccId,new Account(id=oldAccId,of_Sync_to_Marketo_Contacts__c=0));
            }

        }
    }
    }
    
    if (!AccountIds.isEmpty())
    {
      for (AggregateResult aggr : [select count(id),AccountId FROM Contact WHERE Sync_with_Marketo__c=true AND AccountId IN : AccountIds GROUP BY AccountId])
      {
        Account a = Summary.get((Id)aggr.get('AccountId'));
        a.of_Sync_to_Marketo_Contacts__c = (Decimal)aggr.get('expr0');
        Summary.put(a.Id,a);
      }
      
      update Summary.values();
    }    
}


===========

And one more suggesition is add the boolean in the Account Trigger to stop the Unwanted Recurssion

1. Create the Below Apex Class in your Org:
public class RecursiveHandler
{
public static Boolean IsNotRecursive = true;
}

2. Call the above static boolean in your Account Trigger:

trigger UpdateRelatedContacts on Account (after update){
   List<Contact> contactList = new List<Contact>();
   List<Contact> contactList1 = new List<Contact>();
   if(RecursiveHandler.IsNotRecursive){
    RecursiveHandler.IsNotRecursive = false;

    for(Account acc: [SELECT Id,RecordTypeID,(SELECT Id,RecordTypeID FROM Contacts) FROM Account WHERE Id in: Trigger.new]){
        If(acc.Contacts.size()>0 && (acc.RecordTypeID =='012E0000000PR3u' || acc.RecordTypeID =='012440000002T4h')){
         contactList.addALL(acc.Contacts);
        }
    }
    for(contact c:contactList){
        If(c.RecordTypeID !='012440000002T6J'){
           c.checkbox__c=true;
           contactList1.add(c);
    }
    }

    if(contactList1.size() > 0)
    update contactList1;

   }
}

Can you please Let me know if it helps or not!!!

If it helps don't forget to mark this as a best answer!!!


Thanks,
Maharajan.C


 
Chuck M RohllfChuck M Rohllf
Thanks for all your help with this!  I've gone another direction and have combined and condensed where possible.  My updated trigger is functional, however, when I attempt to update Accounts via data loader, I'm getting timeout errors.  I'm far from a developer - is there anything I can do to my trigger below to clean it up and make it more efficient?
trigger AccountGetTerritoryReference on Account (before insert, before update) {

    Map<String,Territory_Reference__c> Terr2Terr_SA = new Map<String,Territory_Reference__c>();
    Map<String,Territory_Reference__c> Terr2Terr_DTT = new Map<String,Territory_Reference__c>();
    
    Set<String> SA = new Set<String>();
    
    for(Account a:Trigger.new)
    {
        if (a.RecordTypeId == '012E0000000PR3u')
    {
        if (a.Agency__c<>null){
            //something new
            SA.add(a.Agency__c);
            a.Agency_Name__c = a.Agency__c; // the default agency name will be the SA
        }
        
        else if (a.Agency__c==null )
        {
            a.Agency_Name__c = '';  
        }
       
    }
                a.NAIS_VOSS_Mgr__c = null;
                a.NAIS_VSEN_Mgr__c = null;
                a.MSO__c = null;
                a.PSE_Representative__c = null;
                a.PSM__c = null;
                a.RMOS__c = null;
                a.VOSS__c = null;
                a.VPSO__c = null;
                a.MTSS__c = null;
                a.Xerox_Entity__c = null;
                a.Area_Title_t2t__c = null;      
                a.ASD__c = null;
                a.CBM__c = null;
                a.VSPM__c = null;
                a.MPS_Manager__c = null;
    
    
    }
    
    for (Territory_Reference__c t : [SELECT MTSS__c,NAIS_VOSS_Mgr__c,VSEN__c,NAIS_VSEN_Mgr__c,AGENT_DTT__c,AGENCY__c,AGENCY_ID__c,SA_DTT_KEY__c,VOSS__c,PSE__c,PSM__c,MSO__c,RVP__c,Area_Title__c,REGION__c,RMOS__c,ASD__c,CBM__c,VPSE__c,VSPM__c FROM Territory_Reference__c WHERE AGENCY_ID__c IN : SA]){
        Terr2Terr_SA.put(t.AGENCY_ID__c,t);
        Terr2Terr_DTT.put(t.AGENT_DTT__c,t);      
    }
    

    if (!Terr2Terr_SA.isEmpty())
    {
        for(Account a:Trigger.new)
        {

            if (Terr2Terr_DTT.get(a.Dtt__c)<>null){
                Territory_Reference__c  t2t = Terr2Terr_DTT.get(a.Dtt__c);
                a.Agency_Name__c = t2t.AGENCY__c;
                a.Inside_Sales_Representive__c = t2t.VSEN__c;
                a.VPSE__c = t2t.VPSE__c; 
            }
            if (Terr2Terr_SA.get(a.Agency__c)<>null){
                Territory_Reference__c  t2t = Terr2Terr_SA.get(a.Agency__c);
                a.Agency_Name__c = t2t.AGENCY__c;  


                a.NAIS_VOSS_Mgr__c = t2t.NAIS_VOSS_Mgr__c;
                a.NAIS_VSEN_Mgr__c = t2t.NAIS_VSEN_Mgr__c;
                a.MSO__c = t2t.MSO__c;
                a.PSE_Representative__c = t2t.PSE__c;
                a.PSM__c = t2t.PSM__c;
                a.RMOS__c = t2t.RMOS__c;
                a.VOSS__c = t2t.VOSS__c;
                a.VPSO__c = t2t.RVP__c;
                a.MTSS__c = t2t.MTSS__c;
                a.Xerox_Entity__c = t2t.REGION__c;
                a.Area_Title_t2t__c = t2t.Area_Title__c;      
                a.ASD__c = t2t.ASD__c;
                a.CBM__c = t2t.CBM__c;
                a.VSPM__c = t2t.VSPM__c;
                
                       
}
        
        List<Contact> contactList = new List<Contact>();
   List<Contact> contactList1 = new List<Contact>();
for(Account acc: [SELECT Id,RecordTypeID,(SELECT Id,RecordTypeID FROM Contacts) FROM Account WHERE Id in: Trigger.new]){
   If(acc.Contacts.size()>0 && (acc.RecordTypeID =='012E0000000PR3u' || acc.RecordTypeID =='012440000002T4h')){
     contactList.addALL(acc.Contacts);
    }
}        
       
        
        for(contact c:contactList){
    If(c.RecordTypeID !='012440000002T6J'){
       c.checkbox__c=true;
       contactList1.add(c);
       //a.NAIS_VOSS_Mgr__c = t2t.NAIS_VOSS_Mgr__c;
                //a.NAIS_VSEN_Mgr__c = t2t.NAIS_VSEN_Mgr__c;
                c.CBM__c = a.MSO__c;
               // a.PSE__c = t2t.PSE__c;
              //  a.PSM__c = t2t.PSM__c;
               // a.RVP__c = t2t.RMOS__c;
              //  a.VOSS__c = t2t.VOSS__c;
              //  a.RVP__c = t2t.RVP__c;
               // a.MTSS__c = t2t.MTSS__c;
             //   a.Xerox_Entity__c = t2t.REGION__c;
              //  a.Area_Title_t2t__c = t2t.Area_Title__c;      
              //  a.ASD__c = t2t.ASD__c;
              //  a.CBM__c = t2t.CBM__c;
             //   a.VSPM__c = t2t.VSPM__c
}
}
update contactList1;
}
 }       
        
        
        
        
        
        
        
        
    
    

}