2

Im trying to make a callout when an opportunity reaches a stage and from this callout update a field. I created this apex class which is called from a flow. But I keep getting You have uncommitted work pending. Please commit or rollback before calling out I found these answers already 1 , 2 but those don't require to return a value from the calllout. I suspect that is the prolbem.

Here someone says its possible to first to the call out and then do the DML transaction. That is exactly what I am doing. I understand invocable methods are bulkified. Is this a problem?

Is there a way to achieve this?

code:

global class CreateProject {
    
      
    @InvocableMethod 
    public static void run(List<List<String>> inputvar){
        String projectName = '';
        String accountName = '';
        
        for(List<String> vars: inputvar){
            
            projectName = vars.get(0);
            accountName = vars.get(1);
        }

        String clientId = '';
        if(getAllClients().containsKey(accountName)){
            clientId = getAllClients().get(accountName);
        }else{
            clientId = insertClient(accountName);
        }
        insertProject(projectName, clientId, accountName);

    }
    public static map<String,String> getAllClients(){
            String key = '';
        String workspaceId = '';
        String baseEndPoint = '';
        HttpRequest req = new HttpRequest();
        String endPoint = baseEndPoint+'/workspaces/' + workspaceId+'/clients?page-size=500';
        req.setEndpoint(endPoint);    
        req.setMethod('GET');
        req.setHeader('X-Api-Key', key);
        Http http = new Http();
        HTTPResponse res = http.send(req);
        Map<String, String> clients = new Map<String, String>();
        
        JSONParser parser = JSON.createParser(res.getBody());
        while (parser.nextToken() != null) {
            String ID = '';
            String name ='';
            if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && 
            (parser.getText() == 'name')) {
                parser.nextToken();
                ID = parser.getText();
                
            }
            if ((parser.getCurrentToken() == JSONToken.FIELD_NAME) && 
            (parser.getText() == 'id')) {
                parser.nextToken();
                name = parser.getText();
            }
            clients.put(name,ID);
        }
        return clients;
    }
    
    public static String insertClient(String name){
        // post method returning id 
        return insertId;
    }
    
    @future(callout=true)
    public static void insertProject(String projectName , String clientId, String accountName){
        // post mthod using id and retrieving anther id which needds to be inserted.
        
        try{
            Opportunity oppty = [select id from opportunity where name=:accountName];
            oppty.API__C = insertId;
            update oppty;
        }catch(QueryException e){
            
        }
    }

}



4
  • Any chance that there is a DML to write logs/update refresh token etc before an actual callout?
    – kurunve
    Commented Feb 17, 2021 at 11:19
  • You're calling getAllClients twice; does this mean it'll do two callouts, or are you caching the result? If it does two callouts, are you sure that method doesn't also do DML (like kurunve said, to log or update tokens) Commented Feb 17, 2021 at 11:43
  • @rael_kid I made two callouts, corrected it to one but that did not solve the problem. I also edited my question to include the method code Commented Feb 17, 2021 at 15:28
  • Is this a Screen, Autolaunched or Triggered Flow?
    – Moonpie
    Commented Feb 17, 2021 at 15:53

1 Answer 1

2

The Spring '21 Release Readiness Live broadcast for Salesforce Flow (https://www.salesforce.com/video/16254720/) has a good overview (from ~17:00 to ~24:00) of Flow transactions and general reasons for receiving this error.

Here is a screenshot from one of the slides from the video, in the context of a Screen Flow:

enter image description here

Similar reasons occur in Autolaunched and Triggered Flows.

Screen Flows

A 'fix' was added in Spring '21 for Screen Flows: a Transaction Control setting. For @InvocableMethods with the callout=true modifier, you can let the Flow decide whether it is necessary to commit the current transaction before executing the callout. If it determines it is necessary, it will commit, and so you should not receive that error. The Transaction Control also lets you select for the Flow to always commit (or always not commit). Here is Salesforce's Apex Developer Guide explanation: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_forcecom_flow_invocable_action_callout.htm

So if you are calling this from a Screen Flow, you should be able to add the (callout=true) modifier to your @InvocableMethod and then make the appropriate Transaction Control selection on the Action step in your Flow.

Triggered Flows

If you are calling this from a Triggered Flow, the workaround is to add a "dummy" local Action step, which causes a transaction commit, just prior to your callout Action step. You can achieve this using UnofficialSF's 'Commit Transaction' Action (https://unofficialsf.com/use-the-committransaction-action-to-get-more-from-your-flow-limits/), or creating your own empty local Action.

Autolaunched Flows

If you are calling this from an Autolaunched Flow, you can - as quoted directly from the Unofficial SF page discussing their 'Commit Transaction' Action states -

...use a Pause element and give it a time of 0 to get the transaction to close.

Add this Pause element just prior to your callout Action step.

You must log in to answer this question.

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