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
Michael MMichael M 

System.AsyncException: Future method cannot be called from a future or batch method: EMedCalloutsExtension.makePostCallout2(List<Id>, String)

I am getting this error message: 

caused by: System.AsyncException: Future method cannot be called from a future or batch method: EMedCalloutsExtension.makePostCallout2(List<Id>, String)

Trigger.ReferralCreateContact: line 35, column 1: []

Class.EMedCalloutsExtension.makePostCallout1: line 77, column 1


How can I fix it?

Here is my code  (trigger and class)

TRIGGER
    trigger  ReferralCreateContact on Lead (after insert, after update) { 
        if (trigger.isInsert){
     List<string> leadIds = new List<string>();
    for (lead ref : trigger.new){
      if(system.isFuture()) return;
        if (ref.Epaces_Checked__c != true && (ref.Emed_Request_Id__c == null || ref.Emed_Request_Id__c == '')){
          lead newl = new lead();
          newl.Id = ref.id;
          leadIds.add(newl.id);
         EMedCalloutsExtension.makePostCallout1(leadIds); 
        }
    }}
    
    if (trigger.isUpdate){
      List<string> leadIds = new List<string>();
        for (lead ref: trigger.new){
              //   if(system.isFuture()) return;
            if (ref.Epaces_Checked__c != true && ref.Emed_Request_Id__c != null && trigger.oldmap.get(ref.id).emed_request_id__c == null){
           lead newl = new lead();
          newl.Id = ref.id;
          leadIds.add(newl.id);
         EMedCalloutsExtension.makePostCallout2(leadIds, ref.Emed_Request_Id__c); 
            }
            
        }
    }

CLASS
public class EMedCalloutsExtension {

    public final Lead referral;
    public String requestLabel;
    public String result {get;set;}
    public List<Object> emedData {get;set;}
    
     @future(callout = true)
       public static void  makePostCallout1(list<id> refIds) {
   List <Lead> refs =[Select Id, firstname, lastname, gender__c, patient_dob__c, patient_ssn__c from Lead where Id in :refIds]; 
     List<lead> reftoupdate = new list<lead>();   
           for (lead ref : refs){
           system.debug('***NAME OF REFERRAL***: '+ ref.firstname + ' ' + ref.lastname);
    String ssnReplace; 
           if (ref.Patient_SSN__c != null){
            ssnReplace = ref.Patient_SSN__c.replace('-','');
           }
    String genderLetter;
           if (ref.Gender__c == 'Male'){
               genderLetter = 'M';
           } else if (ref.Gender__c == 'Female'){
               genderLetter = 'F';
           }
           
           String first;
           String second;
           String third;
            String d = String.valueOf(ref.Patient_DOB__c);
           system.debug('***dob string raw: ' + d);
                first = d.substring(0,4);
                second = d.substring(5,7);
                third = d.substring(8,10);
           String dob = first + second + third;
           system.debug('***dob formatted: ' + dob);


 //    Continuation con = new Continuation(40); //number is timeout in seconds
// con.continuationMethod='processResponse';

//below returned 200 success code in anonymous block  
 string reqbody;
     StaticResource r =[Select Id,Body from StaticResource where Name='EMedCalloutBody' limit 1];
      reqBody=r.body.toString();         
       HttpRequest req = new HttpRequest();
           req.setHeader('Content-Type', 'application/json');
                req.setMethod('POST');
                req.setBody(reqBody);
                req.setEndpoint('callout:Emed');
                req.setTimeout(2 * 60 * 1000);
system.debug('ENDPOINT: ' + req.getendpoint());
  //  system.debug('FULL REQUEST: ' + req);
         system.debug('BODY: '+ req.getBody());
         Http http = new Http();
           HttpResponse response = http.send(req);
        if (response.getStatusCode() != 200) {
            System.debug('The status code returned was not expected: ' +
                response.getStatusCode() + ' ' + response.getStatus());
        } else {
            system.debug(response.getstatuscode());
           System.debug(response.getBody());
             string requestId = response.getbody().substringAfter('"request_id": ').substringBefore(',');
            
            ref.Emed_Request_Id__c = requestId;
            reftoupdate.add(ref);
            
        

        }
// this.requestLabel = con.addHttpRequest(req);
          system.debug('FULL REQUEST: ' + req);
// return con;   
       } update reftoupdate;
           return;
       }
   
    
    
     @future(callout = true)
       public static void  makePostCallout2(list<id> refIds, string reqId) {
  
       List <Lead> refs =[Select Id, firstname, lastname, gender__c, patient_dob__c, patient_ssn__c from Lead where Id in :refIds]; 
           for (lead ref : refs){
              string reqbodyResp;   
              StaticResource r2 =[Select Id,Body from StaticResource where Name='EmedResponseBody' limit 1];
               reqbodyResp=r2.body.toString();
              reqbodyResp=reqbodyResp.replace('{{requestId}}', reqId);
                  HttpRequest req2 = new HttpRequest();
           req2.setHeader('Content-Type', 'application/json');
                req2.setMethod('POST');
                req2.setBody(reqbodyResp);
                req2.setEndpoint('callout:Emed_Response');
                req2.setTimeout(2 * 60 * 1000);
            system.debug('ENDPOINT2: ' + req2.getendpoint());
            system.debug('BODY2: '+ req2.getBody());
             Http http2 = new Http();
           HttpResponse response2 = http2.send(req2);
        if (response2.getStatusCode() != 200) {
            System.debug('The status code returned was not expected: ' +
                response2.getStatusCode() + ' ' + response2.getStatus());
        } else {
            system.debug(response2.getstatuscode());
           System.debug(response2.getBody());
lainTextBody(response2.getBody());

        
        }
               
           }
       }
    
    
}


 
AbhishekAbhishek (Salesforce Developers) 
Salesforce doesn't allow a future method to be called from another future method or a batch job. Before calling your future method, you should check if a future or batch job is already running. This would be a best practice for any code you ever write that calls a future method. The good news is that it's very easy. 

Here's an example:

if(System.IsBatch() == false && System.isFuture() == false){ 
    // make your future call here 
}


For further reference, you can check this discussion too  (https://salesforce.stackexchange.com/questions/102196/error-future-method-cannot-be-called-from-a-future-or-batch-class/102200)

I hope you find the above information is helpful. If it does, please mark as Best Answer to help others too.

Thanks.
Abhishek BansalAbhishek Bansal
Hi Michael,

It is not allowed to call the future method from the Batch Apex, if you still want to call it then you need to write a web service which can call the futire method. Complete steps are given below:
https://www.pebibits.com/future-method-asynchronous-apex/

Let me know if you need more information on this.

Thanks,
Abhishek Bansal.
Michael MMichael M
Hi Abhishek,

The problem in my scenario is that I need to really make 2 future callouts. First I need to make a POST callout, and the response from that callout is a request id. Then, I need to take that request id and make another POST callout until I get a response. 

According to the API provider, I should only send the 1st POST request once, and then I should keep sending the 2nd POST until I receive a response. 

So in my code, I have an "after insert" trigger which sends the 1st POST, and also makes an update to my Lead record with the request id. Then, in my "after update" trigger, I send the 2nd POST with the request id. The problem is that both methods need to be @future callouts. If I remove the @future from the 2nd callout, then when I try to insert a new lead I get an error stating that "Callout from triggers are currently not supported." But when I leave the @future on the 2nd POST,  I this error "Future method cannot be called from a future or batch method". When I add if(System.IsBatch() == false && System.isFuture() == false) to the trigger, the 2nd POST doesn't fire, becaues it is triggered from the update of the first future callout. 

What would you recommend?
Onur Kaya 9Onur Kaya 9
Hi Michael,

For the first future call you can use a trigger, second code should be batch apex that makes Http callout. Here is an example

https://developer.salesforce.com/forums/?id=9060G000000BdVjQAK