Skip to main content

How to Fix "You Have Uncommitted Work Pending" Error in Salesforce

When developing custom backend integrations in Salesforce Apex, developers frequently encounter the runtime exception: System.CalloutException: You have uncommitted work pending. Please commit or rollback before calling out. Understanding the underlying transaction management rules in Salesforce makes resolving this error straightforward.

In plain words: Salesforce does not allow you to open a long-running web request (HTTP callout) while database changes (DML operations like insert or update) are waiting to be saved in the same transaction. You must either complete all callouts before modifying database records or push the callout into a separate asynchronous context.

Understanding the Error

Salesforce enforces strict transaction isolation to prevent database connections from hanging open while waiting for external network responses. If an Apex execution thread performs a DML operation (inserting, updating, or deleting records) and subsequently attempts an HTTP callout within the same synchronous thread, the platform throws an uncommitted work exception.

Developer Trap: Incorrect Execution Sequence

Executing DML statements prior to issuing a web service call in the same thread guarantees a runtime exception:

public class MySalesforceClass {
    public void performDmlAndCallout() {
        // Step 1: DML Operation (Locks the transaction)
        Account acc = new Account(Name = 'Test Account');
        insert acc;

        // Step 2: HTTP Callout (Fails due to uncommitted DML above)
        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://api.example.com/data');
        req.setMethod('GET');
        
        // Throws: System.CalloutException: You have uncommitted work pending
        HttpResponse res = new Http().send(req); 
    }
}

Solution 1: Reorder Operations (Make Callouts First)

The simplest approach when designing synchronous logic is structuring your methods so that all external API integrations execute and complete prior to performing any database DML operations.

Best Practice Sequence: Retrieve external response data first, construct or update your SObject records using that data, and commit your DML statements at the very end of the execution.
public class MySalesforceClass {
    public void performCalloutThenDml() {
        // Step 1: Perform Callout FIRST
        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://api.example.com/data');
        req.setMethod('GET');
        HttpResponse res = new Http().send(req);

        // Step 2: Process response and perform DML LAST
        if (res.getStatusCode() == 200) {
            Account acc = new Account(Name = 'Test Account');
            insert acc; // Safe to execute here
        }
    }
}

Solution 2: Use Asynchronous Processing (@future or Queueable)

If DML operations must execute first—or if the logic runs inside triggers or batch processes where callouts cannot be placed ahead of record saves—isolate the callout into a separate asynchronous transaction using @future(callout=true) or a Queueable Apex job.

public class MySalesforceClass {
    public void performDmlAndCallout() {
        // Step 1: Perform DML in current synchronous transaction
        Account acc = new Account(Name = 'Test Account');
        insert acc;

        // Step 2: Delegate callout to a separate asynchronous thread
        doCalloutAsync(acc.Id);
    }

    @future(callout=true)
    public static void doCalloutAsync(Id accountId) {
        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://api.example.com/data?id=' + accountId);
        req.setMethod('GET');
        
        HttpResponse res = new Http().send(req);
        // Handle API response asynchronously...
    }
}
Core Rule: Never hold open database transaction locks during external web request roundtrips!
Key Summary: Resolving Uncommitted Work Errors
  • Root Cause: Performing DML statements before making synchronous HTTP/SOAP callouts in the same Apex transaction.
  • Fix Option A: Reorder your Apex workflow to perform all external HTTP request callouts before running DML operations.
  • Fix Option B: Annotate your callout method with @future(callout=true) or implement Queueable, Database.AllowsCallouts to force the request into its own thread context.

Conclusion

Salesforce enforces governor limits around uncommitted work to preserve system stability and transactional security. By structuring Apex execution to process external web service requests before database operations—or by offloading calls to asynchronous Apex jobs—you prevent transaction lock collisions and ensure smooth API integrations.