I have written inline Vf page to update the record value. The values are coming from a web service. While updating the record the detail page redirected to VF page. But the record was committed in the database with the error in debug log

"You have uncommitted work pending. Please commit or rollback before calling out"

I want to save the value in the record and the page remains in the same detail page. How could I achieve this?


public with sharing class  ServiceOptionsChange_CLS{ 

public string strId;
public String strOppid{get;set;}
public String strModelCode {get;set;}
public String strVersionCode {get;set;}
public List<SelectOption> lstOptions {get; private set;}
List<String> lstC2Goptions= new List<String>();
public String strop{get;set;}
public Service__c objService{get;set;}
public boolean refreshPage{get;set;}
public String strCode {get;set;}
public String strLabel {get;set;}
public Map<String,Double> mapOptionCodePrice = new Map<String,Double>();

public ServiceOptionsChange_CLS(ApexPages.StandardController controller) {
    objService = new Service__c();
    Service__c objService=[select Opportunity_car_set__c from Service__c where id=:strId]; 
    Opportunity_car_set__c opp=[SELECT Model__r.ProductCode,Version__r.version_code__c,Country_Code__c FROM Opportunity_car_set__c where id=:strOppid];    
    strModelCode    = opp.Model__r.ProductCode;
    strVersionCode  = opp.Version__r.version_code__c;
    system.debug('<<<<<selected value'+strModelCode+''+strVersionCode);


public List<SelectOption> getOptions(){

   OptionDetails_CLS optionValue=new OptionDetails_CLS();
    if(String.isNotBlank(strModelCode) && String.isNotBlank(strVersionCode)){
        lstOptions = new List<SelectOption>();
        lstOptions.add(new SelectOption('','--Select--'));
        for(String strC2Goptions:lstC2Goptions) {
            lstOptions .add(new SelectOption(strC2Goptions,strC2Goptions));
  return lstOptions ;

public Pagereference customSave(){

    String strQuantity=String.valueof(objService.Quantity__c);
    system.debug('<<<<<selected value in save'+strop+'quanity'+strQuantity);
    strLabel =strop.substringafter('-').trim();
    Double dbPriceHT;
    OptionDetails_CLS optionValues =new OptionDetails_CLS();     
    mapOptionCodePrice = optionValues.getOptionPrice(strModelCode,strVersionCode);
    if (mapOptionCodePrice.containsKey(strCode)) {
        dbPriceHT = mapOptionCodePrice.get(strCode);
    if (dbPriceHT!=null) {

        update objService;
        system.debug('<<<<<objService up'+objService);
        return null;
    catch(Exception ex){
        System.debug('Error in SOQL Fetching'+ex);
        ApexPages.Message msg = new ApexPages.Message(Apexpages.Severity.ERROR, ex.getMessage() );
       return null;



<apex:page standardController="Service__c" extensions="ServiceOptionsChange_CLS" sidebar="true" >
<apex:form >
<apex:pageBlock >
<apex:pageblockbuttons location="bottom">
<apex:commandbutton value="Save" action="{!customSave}" /> 
</apex:pageblockbuttons> <apex:pageblocksection columns="2" title="Main Information" >
<apex:selectList size="1" value="{!objService.CodeC2G__c}" >
<apex:selectOptions value="{!Options }" > </apex:selectOptions>
<apex:actionSupport event="onchange" action="{!customSave}" reRender="a" />
<apex:inputfield value="{!objService.Quantity__c}" required="false" />
<apex:pageblocksectionitem />

6 Answers 6


The issue occurs when you first perform DML and then callout in the same transaction

check this doc

You have uncommitted work pending. Please commit or rollback before calling out

You need to perform Callout in the future method so your callout and DML will be in the different transaction

This way you can resolve this issue

create a list and serialize it to pass to the future method. Because we can't pas list of sobject as a parameter in the future method

insert listofAccount;

string jsonString = JSON.serializePretty(listofAccount );


In future method make callout

@future(callout = true)
public static void callout(string jsonString){
    //do callout here rest or soap

this way you can solve this issue.

  • 2
    Actually, the documentation linked to in this answer explains that the "Web Service Callout may not occur after a DML statement". So the issue is caused by executing DML prior to the callout, not after. The author of the answer has it backwards.
    – SF1Dev
    Oct 25, 2018 at 14:20
  • @SF1Dev thanks for bringing in my notice. Updated answer now
    – Ratan Paul
    Oct 26, 2018 at 3:32

So many wrong answers here... You definitely CAN do a callout and then DML in same transaction. You cannot do it THE OTHER WAY AROUND. Only Oles Malkov got it right.

Also switching to @future may not always help, as Asnyc Apex also may result in the same exception:


  • Ratan Paul's answer has been updated to correct the order. Sep 18, 2019 at 9:55
  • I actually encountered this error, when I was trying to perform a DML AFTER a callout. So the answers are not entirely wrong. After commenting out the DML statement (that was one line after the HttpResponse res = new Http().send(req), the code worked. Feb 5, 2020 at 11:05

I just faced this error and after checking the code I found out that I was sending an email before a callout. This was the reason of the error. This answer does not solve the problem, just wanted to share hoping it will help someone.


Rather than a @future method that may not work as @Jo_gurt said and as stated in the Sf doc.

A workaround could be to use a flow with the option let the flow decide (the flow will decide to make a new transaction) in transaction control.

The flow will call a class with the @InvocableMethod and callout=true flags.


The only way I was able to solve this was to move my upsert into it's own Apex class. This code would not work from a Controller I built to handle all my Rest API callouts.

Public class Helpers
    public static void SaveToken(string currentToken, DateTime expires)
            //Save Token
            string p1 = currentToken.substring(0,255);
            string p2 = currentToken.substring(255,263);

            COMPANY_Settings__c setting = COMPANY_Settings__c.getOrgDefaults();
            setting.TokenP1__c = p1;
            setting.TokenP2__c = p2;
            setting.Expires__c = expires;
            upsert setting;

I'm calling this method from my last callout response getStatusCode method like so. This is the only way I know that all my callouts have completed.

if (response2.getStatusCode() == 200) {
  Helpers.SaveToken(AppClaim.Token, AppClaim.Expires);

I'm new to Apex and Salesforce coding, so this was so frustrating to say the least. It makes no sense why this method would not work, as the instructions state, with the main Controller that I'm using. However, I like this method better, because I'm hoping that I can do all my database upserts, inserts etc... using this Helper class. Hope this helps someone else. No wonder there are so many questions on this littered all over the Salesforce forums.

I also had to split the Token into two strings (over 256), which was also frustrating. :)

  • 3
    It's not the separate class that makes the difference; it's the @future annotation, which pushes the DML into a separate transaction from the callout. See Ratan's answer for more details.
    – David Reed
    Aug 6, 2018 at 15:45
  • The @future method did not matter. It would not work in the main Controller I built. Trust me on this. I refactored this over and over. As you can imagine, I need the Token to do all my API calls before the entire process finishes. This results in needless calls to get a Token the first time the app runs before I can finally save the Token and re-use it before it expires. This is ridiculously inefficient. Thanks for the comment. If you know of a better way to do this, please let me know. Ideally, I would like to save the initial callout to get the Token. I imagine it will fail, though. Aug 6, 2018 at 15:56
  • David's comment (and Ratan's answer, although there are some best practice concerns) is correct here. Salesforce does not allow DML (insert, update, upsert, delete, undelete) in a given transaction after a callout is performed, and the function of the @future annotated method is to ensure that your DML is in a separate transaction. That aside, it sounds to me like you have some material here for a couple of good questions. I'd encourage you to ask some questions about your code and drink from the well of knowledge that is SFSE.
    – Derek F
    Aug 6, 2018 at 17:27
  • Derek, thanks for the comment. It will be interesting to see what happens when I batch this process seeing as I have to save the Token at the end if in fact a new Token was issued due to the old one expiring. Aug 6, 2018 at 20:34
  • 2
    Isn't this working simply because the upsert is happening after the callout?
    – Shanerk
    Feb 6, 2020 at 16:29

The test method for my callout was encountering the same error. The issue arose because the test class was inserting data prior to invoking the callout method. (I used @testSetup annotation.)

It took some time for me to diagnose the problem. However, once I wrapped my actual test method with Test.startTest() and Test.stopTest(), the issue was resolved.

Additionally, here is a new Salesforce help article on this error: https://help.salesforce.com/s/articleView?id=000385708&type=1

You must log in to answer this question.

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