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
ankushankush 

"Future method cannot be called from a future" error in trigger

 Getting the error when validating
Future method cannot be called from a future or batch method: contactactivecheckboxclass.updatecnt(Set<String>) Trigger.Checkuseractive: line 18, column 1

ankush
Hi Tejpal,

       Tried the below code using your code given above the contactactivecheckboxclass1 error vanished but now the contactactivecheckboxclass causing the issue. Same issue. Can you please help me in the contactactive checkbox class if I had made any error.



Trigger.

//This is a trigger that is used to check whether the user is active and updates the related Contact
trigger Checkuseractive on User (after insert, after update) {
    set<string> set1 = new set<string>();
    set<string> set2 = new set<string>();
    if (!ProcessorControl.inFutureContext) {
        //Below soql query gets the IsActive and ContactId of the user
        list<user> u=[select id,Isactive,contactid from user where id=:trigger.new];
        for(user usr:u){
            if(usr.IsActive==false)
            {
            set1.add(usr.ContactId);
            }
            else if(usr.IsActive==true)
            {
            set2.add(usr.ContactId);
            }
        }
        contactactivecheckboxclass.Updatecnt(set1);
        contactactivecheckboxclass1.Updatecnt1(set2);
    }
}

Contactactive checkbox class
//Future class for updating Contact Active Checkbox field
public class contactactivecheckboxclass {
    @future
    //Method that takes the contact id as parameter from Trigger and checks if user is active on user Object and updates the related contact accordingly.
    public static void updatecnt(set<string> x){
        try{
            list<contact> cntlist= new list<contact>();
            list<contact> c=[select Is_user_Active__c from Contact where id IN : x limit 1];
            for(contact cnt:c){
            cnt.Is_User_Active__c=false;
            if(!test.isrunningtest()){
                cntlist.add(cnt);
            }
            }
            ProcessorControl.inFutureContext = true;
            database.update(cntlist);
        }
        catch(exception e){
        }
    } 
}

Contact Active checjboxclass1

//Future class for updating Contact Active Checkbox field
public class contactactivecheckboxclass1 {
    @future
    //Method that takes the contact id as parameter from Trigger and checks if user is active on user Object and updates the related contact accordingly.
    public static void updatecnt1(set<string> x){
        try{
            list<contact> cntlist= new list<contact>();
            list<contact> c=[select Is_user_Active__c from Contact where id IN : x limit 1];
            for(contact cnt:c){
            cnt.Is_User_Active__c=true;
            if(!test.isrunningtest()){
                 cntlist.add(cnt);
            }
            }
             ProcessorControl.inFutureContext = true;
            database.update(cntlist);
            }
        
        catch(exception e){
        }
    } 
}

Processor Control Class

public class ProcessorControl {
    public static boolean inFutureContext = false;
}
 
pconpcon
The problem is that youar enot checking to see if you are in your cuture context before calling your future method.  If you update your trigger to the following, it should fix your issue.
 
trigger Checkuseractive on User (after insert, after update) {                                                                                                   
    set<string> set1 = new set<string>();
    set<string> set2 = new set<string>();

    if (!ProcessorControl.inFutureContext) {
        //Below soql query gets the IsActive and ContactId of the user

        for (User usr : [
            select IsActive,
                ContactId
            from User
            where Id = :trigger.new
        ]) {
            if (!usr.IsActive) {
                set1.add(usr.ContactId);
            } else if (usr.IsActive) {
                set2.add(usr.ContactId);
            }
        }

        if (
            !set1.isEmpty() && 
            !contactactivecheckboxclass.ProcessorControl.inFutureContext
        ) {
            contactactivecheckboxclass.Updatecnt(set1);
        } 

        if (
            !set2.isEmpty() &&
            !contactactivecheckboxclass1.ProcessorControl.inFutureContext
        ) {
            contactactivecheckboxclass1.Updatecnt1(set2);
        }
    }
}

NOTE: This code has not been tested and may contain typographical or logical errors
NOTE: Please use the "Add a code sample" button (Icon <>) to insert your code.  This will increase readability and make it easier to reference in solutions.
ankushankush
Hi Getting the below error in the trigger can you please guide.
on line 23
Variable does not exist: contactactivecheckboxclass.ProcessorControl.inFutureContext
pconpcon
I'm sorry, I thought you had a ProcessorControl class in both of your classes. I have taken the liberty of updating your code to condense it down into a single future method and make it bulk ready.

Trigger
 
trigger Checkuseractive on User (after insert, after update) {
    Set<Id> activeIds = new Set<Id>();
    Set<Id> inactiveIds = new Set<Id>();

    if (!ModifyContactCheckbox.ProcessorControl.inFutureContext) {
        for (User usr : [
            select IsActive,
                ContactId
            from User
            where Id = :trigger.new
        ]) {
            if (!usr.IsActive) {
                activeIds.add(usr.ContactId);
            } else if (usr.IsActive) {
                inactiveIds.add(usr.ContactId);
            }
        }

        ModifyContactCheckbox.changeContactActiveStatus(activeIds, inactiveIds);
    }
}

ModifyContactCheckbox
public class ModifyContactCheckbox {
    public class ProcessorControl {
        public static boolean inFutureContext = false;
    }

    @future
    public static void changeContactActiveStatus(Set<Id> activateIds, Set<Id> deactivateIds){
        ProcessorControl.inFutureContext = true;

        Set<Id> ids = new Set<Id>();
        ids.addAll(activateIds);
        ids.addAll(deactivateIds);

        if (ids.isEmpty()) {
            return;
        }

        List<Contact> contactList = new List<Contact>();

        for (Contact contact : [
            select Is_user_Active__c
            from Contact
            where id in :ids
        ]) {

            if (activateIds.contains(contact.Id)) {
                contact.Is_User_Active__c = true;
            } else if (deactivateIds.contains(contact.Id)) {
                contact.Is_User_Active__c = false;
            } else {
                continue;
            }

            if (!test.isRunningTest()){
                contactlist.add(contact);
            }
        }

        if (contactList.isEmpty()) {
            update contactList;
        }
    }
}
ankushankush
Hi I tried the code but the class 
ModifyContactCheckbox is now throwing me the nelow error

Only top-level class variables can be declared static

Can you please help me on this
pconpcon
I have verified that the following classes compile.  They may not work as expected, but they should.

Trigger
 
trigger Checkuseractive on User (after insert, after update) {
    Set<Id> activeIds = new Set<Id>();
    Set<Id> inactiveIds = new Set<Id>();

    if (!ModifyContactCheckbox.inFutureContext) {
        for (User usr : [
            select IsActive,
                ContactId
            from User
            where Id = :trigger.new
        ]) {
            if (!usr.IsActive) {
                activeIds.add(usr.ContactId);
            } else if (usr.IsActive) {
                inactiveIds.add(usr.ContactId);
            }
        }

        ModifyContactCheckbox.changeContactActiveStatus(activeIds, inactiveIds);
    }
}

ModifyContactCheckbox
 
public class ModifyContactCheckbox {
    public static boolean inFutureContext = false;

    @future
    public static void changeContactActiveStatus(Set<Id> activateIds, Set<Id> deactivateIds){
        inFutureContext = true;

        Set<Id> ids = new Set<Id>();
        ids.addAll(activateIds);
        ids.addAll(deactivateIds);

        if (ids.isEmpty()) {
            return;
        }

        List<Contact> contactList = new List<Contact>();

        for (Contact contact : [
            select Is_user_Active__c
            from Contact
            where id in :ids
        ]) {

            if (activateIds.contains(contact.Id)) {
                contact.Is_User_Active__c = true;
            } else if (deactivateIds.contains(contact.Id)) {
                contact.Is_User_Active__c = false;
            } else {
                continue;
            }

            if (!test.isRunningTest()){
                contactlist.add(contact);
            }
        }

        if (contactList.isEmpty()) {
            update contactList;
        }
    }
}