2

I have a code where I send email using single email message.

I am setting targetObjectId as a contact Id.

I am getting the following error:

System.EmailException: SendEmail failed. First exception on row 0; first error: UNABLE_TO_LOCK_ROW.

The record for which the error occurs is a Contact.

As per my research, if a record is under process, still we can query its value and use it. Why is this error occurring?

The error doesn't occur always, but is happening regularly.

Below is the method :

public static void sendEmail(List<Id> contactids) 
{ 
    EmailTemplate et = [SELECT id FROM EmailTemplate WHERE developerName = 'XYZ'];
    if(et.Id!= Null)
    {
        List<SingleEmailMessage> emailList = new List<SingleEmailMessage>();
        OrgWideEmailAddress[] owea = [select Id from OrgWideEmailAddress where Address = '[email protected]'];

        for(Id conId: contactids)
        {
            Messaging.SingleEmailMessage mail = new Messaging.SingleEmailMessage();
            mail.setTargetObjectId(conId); 
            mail.setTemplateId(et.Id);
            mail.setOrgWideEmailAddressId(owea.get(0).Id);
            emailList.add(mail);
        }    
    }

    if(!emailList.isEmpty()){
        Messaging.sendEmail(emailList); // I am getting the Unable_To_Lock_Row error here
    }

}
3
  • 1
    Are you receiving this error every time, or only some times? Row locking issues are usually transitory. Do you call setSaveAsActivity() at all, or use FOR UPDATE in a SOQL query? Can you include your email sending code?
    – David Reed
    Mar 13, 2018 at 13:58
  • No, I am not using setSaveAsActivity().The sendEmail method is invoked in a future method though. The problem is, why I am getting this error, because I am just reading the record, after querying. Mar 13, 2018 at 14:23
  • 1
    if(et.Id!= Null) is not necessary here-- if the template is not found, you'll get a QueryException.
    – sfdcfox
    Mar 13, 2018 at 14:53

1 Answer 1

2

The Salesforce Record Locking Cheat Sheet is incredibly useful for troubleshooting this type of issue.

It appears to me that what is happening is your Contact is being briefly locked as a result of inserting either a Task (which is the default behavior if you do not call setSaveAsActivity(false) and causes locking of the records for the Task's WhoId, WhatId, and the related Account) or an EmailMessage, if using Enhanced Email.

Because you're performing this email send in a future method, I am going to speculate that the code calling the future method is making updates to the Contact records. Because the future method is running concurrently with this code, which may be non-bulkified or require a lot of processing time, you're seeing collisions between the two locks that are required. That's the root cause of the row locking error - two parallel processes trying to modify the Contact or modify/insert a qualifying child object of Contact at the same time. The record locking cheat sheet helps clarify which child objects can cause this situation.

It's not obvious without understanding the context in which this code is being called what the best approach is to fix it. One approach could be to move the email sending code out of the future method and run it synchronously, after the other processing takes place, or to fire the future method at the very end of the original processing.

You must log in to answer this question.

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