function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
DM 13DM 13 

SendEmail failed. First exception on row 0; first error: UNKNOWN_EXCEPTION

Hi, 

I have below Apex code to send notification to case owner when an Email is transferred from one case to another. This was working fine before enabling Enhanced Email feature. But once its enbled it's throwing below error -

EXCEPTION - SendEmail failed. First exception on row 0; first error: UNKNOWN_EXCEPTION, EmailMessageTrigger: execution of AfterInsert
caused by: System.NullPointerException: Attempt to de-reference a null object

public static String transferEmailsToTargetCase(Id originCaseId, Id targetCaseId, Set<Id> emailIds) {
        System.debug('** transferEmailsToTargetCase start **');
        Map<Id, EmailMessage> oldEmailId2newEmail = new Map<Id, EmailMessage>();
        Case originCase = [
            SELECT Id, CaseNumber, Subject,
                (SELECT Id, FromAddress, Incoming, ToAddress, Subject, TextBody, ParentId, BccAddress,
                    CcAddress, FromName, Headers, HtmlBody, MessageDate, ReplyToEmailMessageId,
                    Source_Case__c, Status
                FROM EmailMessages
                WHERE Id IN :emailIds)
            FROM Case
            WHERE Id = :originCaseId
        ];
        
        Case targetCase = new case();
        if(targetCaseId!=null){
            targetCase = [
                SELECT Id, ownerId, owner.email, owner.name, CaseNumber, Subject
                    FROM Case
                    WHERE Id = :targetCaseId
            ];
        }
       
        if(originCase.EmailMessages.isEmpty()) {
            return Label.No_emails_under_source_Case;
        }   

        for(EmailMessage em : originCase.EmailMessages) {
            EmailMessage emCloned = em.clone();
            emCloned.ParentId = targetCaseId;
            if(String.isBlank(em.Source_Case__c)) {
                emCloned.Source_Case__c = originCaseId;
            }
            oldEmailId2newEmail.put(em.Id, emCloned);
        }


        try {
            EmailMessageTriggerHelper.CHECK_EDIT_RIGHTS_ON_CASE = false;
            insert oldEmailId2newEmail.values();
            copyOverAttachmentsToNewEmail(oldEmailId2newEmail);
            EmailMessageTriggerHelper.ALLOW_DELETION = true;
            delete originCase.EmailMessages;
            
            //send email to new case owner to confirm emails moved
            if(targetCaseId!=null){
                //update URL for live
                string cssProdOrgURL = Org_Settings__c.getOrgDefaults().ORG_URL__c+'/';
                system.debug('cssProdOrgURL --'+ cssProdOrgURL);
                
                string emailBodyConfirmMove = '<html><head><style> table {font-family: arial, sans-serif;border-collapse: collapse;}td, th {border: 1px solid #dddddd;text-align: left;padding: 8px;}tr:nth-child(even) {background-color: #F1F1F1;}</style></head><body><p>Hi ';
                emailBodyConfirmMove+=targetCase.owner.name+',</p><br/><p>THIS IS A SYSTEM GENERATED EMAIL.</p><p>Please be advised that one or more emails have been moved to the ';
                emailBodyConfirmMove+='<a href="'+cssProdOrgURL+targetCase.id+'">'+targetCase.CaseNumber+'</a> Case, and it requires your review.</p><br/><p>Number of Emails: ';
                emailBodyConfirmMove+=originCase.EmailMessages.size()+'</p><p>Case Subject: ';
                emailBodyConfirmMove+='<a href="'+cssProdOrgURL+targetCase.id+'">'+targetCase.Subject+'</a></p><br/><table ><tr ><th>Subject</th><th>From Name</th><th>From Address</th><th>Message Date</th></tr>';
                
                for(EmailMessage emCloned :oldEmailId2newEmail.values()){
                    emailBodyConfirmMove+='<tr><td><a href="'+cssProdOrgURL+emCloned.id+'">'+emCloned.Subject+'</a></td>';
                    emailBodyConfirmMove+='<td>'+emCloned.FromName+'</td>';
                    emailBodyConfirmMove+='<td>'+emCloned.FromAddress+'</td>';
                    emailBodyConfirmMove+='<td>'+emCloned.MessageDate+'</td></tr>';
                    
                }
                emailBodyConfirmMove+='</table>';
                if(string.valueOf(targetCase.OwnerId).startsWith('005')){//owner is user not queue
                    List<Messaging.SingleEmailMessage> lstMsgs = new List<Messaging.SingleEmailMessage>();
                    Messaging.SingleEmailMessage msg = new Messaging.SingleEmailMessage();
                    user targetOwner = [SELECT Id, email FROM user WHERE id=:targetCase.OwnerId AND email != null];
                    msg.setToAddresses(new List<String>{targetOwner.email});
                    msg.setHTMLbody(emailBodyConfirmMove);
                    msg.setSubject('Emails transferred to CASE -'+targetCase.CaseNumber);

                    lstMsgs.add(msg);
                    Messaging.SendEmailResult[] r = Messaging.sendEmail(lstMsgs);
                    
                }
            }
            
            
        } catch(Exception e) {
  system.debug('** EXCEPTION - '+e.getMessage());
            return e.getMessage();
        }
        
        return Label.Success_msg;
    }
ravi soniravi soni
hi,
try below code. I have modified your code and commented your code.I believe it will work as expact.
public static String transferEmailsToTargetCase(Id originCaseId, Id targetCaseId, Set<Id> emailIds) {
        System.debug('** transferEmailsToTargetCase start **');
        Map<Id, EmailMessage> oldEmailId2newEmail = new Map<Id, EmailMessage>();
		Case originCase;
        /*Case originCase = [
            SELECT Id, CaseNumber, Subject,
                (SELECT Id, FromAddress, Incoming, ToAddress, Subject, TextBody, ParentId, BccAddress,
                    CcAddress, FromName, Headers, HtmlBody, MessageDate, ReplyToEmailMessageId,
                    Source_Case__c, Status
                FROM EmailMessages
                WHERE Id IN :emailIds)
            FROM Case
            WHERE Id = :originCaseId
        ];*/
        for(Case ocase : [
            SELECT Id, CaseNumber, Subject,
                (SELECT Id, FromAddress, Incoming, ToAddress, Subject, TextBody, ParentId, BccAddress,
                    CcAddress, FromName, Headers, HtmlBody, MessageDate, ReplyToEmailMessageId,
                    Source_Case__c, Status
                FROM EmailMessages
                WHERE Id IN :emailIds)
            FROM Case
            WHERE Id = :originCaseId
        ]){
			originCase = ocase;
		}
		
        Case targetCase = new case();
        if(targetCaseId!=null){
			
            /*targetCase = [
                SELECT Id, ownerId, owner.email, owner.name, CaseNumber, Subject
                    FROM Case
                    WHERE Id = :targetCaseId
            ];*/
			for(case tCase : [
                SELECT Id, ownerId, owner.email, owner.name, CaseNumber, Subject
                    FROM Case
                    WHERE Id = :targetCaseId
            ]){
			targetCase = tCase;
			}
        }
       
        if(originCase.EmailMessages.isEmpty()) {
            return Label.No_emails_under_source_Case;
        }   

        for(EmailMessage em : originCase.EmailMessages) {
            EmailMessage emCloned = em.clone();
            emCloned.ParentId = targetCaseId;
            if(String.isBlank(em.Source_Case__c)) {
                emCloned.Source_Case__c = originCaseId;
            }
            oldEmailId2newEmail.put(em.Id, emCloned);
        }


        try {
            EmailMessageTriggerHelper.CHECK_EDIT_RIGHTS_ON_CASE = false;
            insert oldEmailId2newEmail.values();
            copyOverAttachmentsToNewEmail(oldEmailId2newEmail);
            EmailMessageTriggerHelper.ALLOW_DELETION = true;
            delete originCase.EmailMessages;
            
            //send email to new case owner to confirm emails moved
            if(targetCaseId!=null){
                //update URL for live
                string cssProdOrgURL = Org_Settings__c.getOrgDefaults().ORG_URL__c+'/';
                system.debug('cssProdOrgURL --'+ cssProdOrgURL);
                
                string emailBodyConfirmMove = '<html><head><style> table {font-family: arial, sans-serif;border-collapse: collapse;}td, th {border: 1px solid #dddddd;text-align: left;padding: 8px;}tr:nth-child(even) {background-color: #F1F1F1;}</style></head><body><p>Hi ';
                emailBodyConfirmMove+=targetCase.owner.name+',</p><br/><p>THIS IS A SYSTEM GENERATED EMAIL.</p><p>Please be advised that one or more emails have been moved to the ';
                emailBodyConfirmMove+='<a href="'+cssProdOrgURL+targetCase.id+'">'+targetCase.CaseNumber+'</a> Case, and it requires your review.</p><br/><p>Number of Emails: ';
                emailBodyConfirmMove+=originCase.EmailMessages.size()+'</p><p>Case Subject: ';
                emailBodyConfirmMove+='<a href="'+cssProdOrgURL+targetCase.id+'">'+targetCase.Subject+'</a></p><br/><table ><tr ><th>Subject</th><th>From Name</th><th>From Address</th><th>Message Date</th></tr>';
                
                for(EmailMessage emCloned :oldEmailId2newEmail.values()){
                    emailBodyConfirmMove+='<tr><td><a href="'+cssProdOrgURL+emCloned.id+'">'+emCloned.Subject+'</a></td>';
                    emailBodyConfirmMove+='<td>'+emCloned.FromName+'</td>';
                    emailBodyConfirmMove+='<td>'+emCloned.FromAddress+'</td>';
                    emailBodyConfirmMove+='<td>'+emCloned.MessageDate+'</td></tr>';
                    
                }
                emailBodyConfirmMove+='</table>';
                if(string.valueOf(targetCase.OwnerId).startsWith('005')){//owner is user not queue
                    List<Messaging.SingleEmailMessage> lstMsgs = new List<Messaging.SingleEmailMessage>();
                    Messaging.SingleEmailMessage msg = new Messaging.SingleEmailMessage();
                    user targetOwner = [SELECT Id, email FROM user WHERE id=:targetCase.OwnerId AND email != null];
                    msg.setToAddresses(new List<String>{targetOwner.email});
                    msg.setHTMLbody(emailBodyConfirmMove);
                    msg.setSubject('Emails transferred to CASE -'+targetCase.CaseNumber);

                    lstMsgs.add(msg);
                    Messaging.SendEmailResult[] r = Messaging.sendEmail(lstMsgs);
                    
                }
            }
            
            
        } catch(Exception e) {
  system.debug('** EXCEPTION - '+e.getMessage());
            return e.getMessage();
        }
        
        return Label.Success_msg;
    }

let me know by marking it as best answer.
Thank you
Don't forget to mark it as best answer.
ravi soniravi soni
hy DM 13,
I think you should try my code once and if you are satisifed then mark it as best answer.
you best mark give us motivation to help others.
Thank you
DM 13DM 13
Hi Veer,
Thanks for your reply.
I tried to update the code as mentioned in above comment but it's not working still. It is throwing same error and not send email after it's been transferred.
Thank you
DM 13DM 13
Hi,
I have another helper class which has the a method called updateCaseEmailActivity(). And the error shows - 

'EXCEPTION - SendEmail failed. First exception on row 0; first error: UNKNOWN_EXCEPTION, EmailMessageTrigger: execution of AfterInsert
caused by: System.NullPointerException: Attempt to de-reference a null object
Class.EmailMessageTriggerHelper.updateCaseEmailActivity: line 55, column 1
Class.EmailMessageTriggerHelper.onAfterInsert: line 43, column 1'

Below is the EmailMessage helper class - 
public class EmailMessageTriggerHelper {
    
    public static final String PLV_EMAIL_RECEIVED = 'Email Received';
    public static final String PLV_EMAIL_SENT = 'Email Sent';

    public static Boolean CHECK_EDIT_RIGHTS_ON_CASE = true;
    public static Boolean ALLOW_DELETION = false;
    
    /* HANDLERS */
    
    public static void onBeforeInsert(List<EmailMessage> triggerNew)
    {
        if (CHECK_EDIT_RIGHTS_ON_CASE){
            preventEmailSendIfNoEditRightsOnCase(triggerNew);
        }
    }
    
    public static void onBeforeDelete(Map<Id,EmailMessage> oldMap)
    {
        checkDeletePermission(oldMap.values());
    }
    
    public static void onAfterInsert(List<EmailMessage> triggerNew)
    {
        updateCaseEmailActivity(triggerNew);
    }
    
    /* METHODS */
    
    // when an email is created, update case object with datetime and type
    public static void updateCaseEmailActivity(List<EmailMessage> emails)
    {
        Map<Id,Case> updateCases = new Map<Id,Case>();
        
        for(EmailMessage em : emails)
        {
            if(em.ParentId.getSobjectType() == Schema.Case.SObjectType) // if parent is case ----- THROWING EXCEPTION
            {
                // create case object for update
                Case c = new Case(Id = em.ParentId);
                c.Last_Email_Activity__c = em.CreatedDate;
                c.Email_Activity_Type__c = em.Incoming ? PLV_EMAIL_RECEIVED : PLV_EMAIL_SENT;
                if(em.Incoming) {
                    c.Last_Received_Email_Address__c = em.FromAddress;
                }
                
                // in the rare case we get two emails at the same time, update with newer (if that's even possible to ascertain)
                if(!updateCases.containsKey(c.Id)|| updateCases.get(c.Id).Last_Email_Activity__c <= c.Last_Email_Activity__c)
                {
                    updateCases.put(c.Id,c);
                }
            }
        }
        if(updateCases.size() > 0){
        update updateCases.values();
        }
    }

    public static void checkDeletePermission(List<EmailMessage> emails)
    {
        if ((Access_Permissions__c.getInstance().EmailMessageDelete__c == null
            || !Access_Permissions__c.getInstance().EmailMessageDelete__c)
            && !ALLOW_DELETION){
            for (EmailMessage em :emails){
                em.addError(Label.Only_Admin_Can_Delete);
            }
        }
    }
    
    // prevent user from sending email if they don't have edit access on case
    public static void preventEmailSendIfNoEditRightsOnCase(List<EmailMessage> emailMessages)
    {
    Id userId = UserInfo.getUserId();
    Set<Id> caseIds = new Set<Id>();
    for(EmailMessage em : emailMessages)
    {
    caseIds.add(em.ParentId);
    }
    
    // find access
    Set<Id> hasEditAccessIds = new Set<Id>(); // contains ids of all records user can edit
    for(UserRecordAccess ura : [select HasEditAccess, RecordId from UserRecordAccess where UserId = :userId and RecordId in :caseIds])
    {
    if(ura.HasEditAccess)
    {
    hasEditAccessIds.add(ura.RecordId);
    }
    }
    
    for(EmailMessage em : emailMessages)
    {
    if(!hasEditAccessIds.contains(em.ParentId))
    {
    em.addError(Label.COW_Email_NoEditAccessToCase);
    }
    }
    }
}