Print this page

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

Knowledge Article Number 000079772
Description Customer is trying to do a save point, insert acct to find duplicates and rollback and then a webservice callout to send the acct data to an external system. But before the webservice is called customer was getting the error "You have uncommitted work pending. Please commit or rollback before calling out".
 
Their scenario was :-
 
1. User enters info on UI to create a new customer and submits save. 
2. System creates a savepoint and then inserts the customer record . 
3. Triggers fires to find duplicates.
4. If there are no duplicates it will rollback the customer record insert based on save point set earlier. 
5. system will call a webservice to send the new customer data to external system and get externalID back .
6. system stores the new customer in SFDC along with the external ID.


 
Resolution
You can't make callouts, HTTP or otherwise, once you have made changes to the database. It's true that you cannot make callouts with pending transactions in the request context.  Also, it's not possible to do an explicit commit.  So your only way out is to make the callout in a separate context. 
 
You cannot perform a DML operation prior to a callout. All the DML operations should be invoked only after you are done with callouts.So, make a webservice callout first and then save the request.
 
If you are making multiple callouts, then save all the requests in a list or map and post callouts you save them.
 
Basically the following scenario will work :-
 
query
callout
query
callout
insert
 
callout
callout 
callout
insert or update
 
But the following scenario will fail :-
 
callout
insert
callout  <---- fails here

Possible workaround :-
-----------------------------------------------------------------------------------------

1) You either need to commit the transaction, make the callout prior to any database changes or move your callout to an @future method (place @future annotation to the webservice method).

2) Splits the transaction into two separate Ajax processes. The first inserts the record and the second performs the callout and is able to update the newly inserted record.

You can save the records and then respond to the user with a temporary page with a "Loading" message as you make the second call back to perform the callout.  To make it look seamless, you can also make successive  AJAX calls to save records and to make callout as user sees "Loading" message upon clicking "Save" button.

4) Sometimes customer do not want to place @future annotation to the webservice method as they need a response from the webservice to decide if they  have to rollback the insert or not ?

 
They can execute an action to insert the object and then execute the webservice callout on the oncomplete event of a commandButton. Then, return a PageReference giving the user immediate feedback.
If the webservice callout returns any error, then delete the object and returning the user back to the same page.
 
See below:
 
@VisualforcePage

 <apex:actionFunction name="executeWS" action="{!executeWS}"></apex:actionFunction>
 <apex:commandButton value="Save" action="{!save}" oncomplete="executeWS()" />
 
@Controller
 
public PageReference save() {
 insert obj;
}
 
public PageReference executeWS(){
 obj = [SELECT ...];
 try{
  callout ws;
 } catch(System.Exception ex){
  delete obj;
  ApexPages.addMessages(e);
  return null;
 }
 return new PageReference('/' + id);
}




promote demote