I'm trying to cut off email threading from EmailMessage. I try to do this in BEFORE INSERT trigger.

Original email:

Hello Tom,

We saw your complaint. We are working on it.

Gigasoft Support

From: Mark Dou
Sent: Friday, November 30, 2018 10:54 AM
To: Gigasoft
Subject: Re: Email Threading in Cases

Hello Gigasoft Support,

Please advise me how to send emails from your software.

Best regards, Mark Dou

Result email:

Hello Tom,

We saw your complaint. We are working on it.

Gigasoft Support

The problem.
For some reason TextBody of an EmailMessage is processed correctly. But not HtmlBody. TextBody doesn't have all text, but HtmlBody has everything.

public static EmailMessage cutOffQuotedThreadV2(EmailMessage newEmailMessage) {
EmailMessage cutEmailMessage = newEmailMessage.clone(false, true, true, true);

String textDelimiterToSearch, htmlDelimiterToSearch;
for (Case_Email_Threading_Settings__mdt cetsItem : getReplyDelimiters()) {
  textDelimiterToSearch = null;
  htmlDelimiterToSearch = null;

  if (cetsItem.Delimiter_End__c != null) {
    if (cutEmailMessage.TextBody != null && cutEmailMessage.TextBody.substringBetween(cetsItem.Delimiter_Begin__c, cetsItem.Delimiter_End__c) != null) {
      textDelimiterToSearch = cetsItem.Delimiter_Begin__c + cutEmailMessage.TextBody.substringBetween(cetsItem.Delimiter_Begin__c, cetsItem.Delimiter_End__c) + cetsItem.Delimiter_End__c;
    if (cutEmailMessage.HtmlBody != null && cutEmailMessage.HtmlBody.substringBetween(cetsItem.Delimiter_Begin__c, cetsItem.Delimiter_End__c) != null) {
      htmlDelimiterToSearch = cetsItem.Delimiter_Begin__c + cutEmailMessage.HtmlBody.substringBetween(cetsItem.Delimiter_Begin__c, cetsItem.Delimiter_End__c) + cetsItem.Delimiter_End__c;
  } else {
    textDelimiterToSearch = cetsItem.Delimiter_Begin__c;
    htmlDelimiterToSearch = cetsItem.Delimiter_Begin__c;

  if (cutEmailMessage.TextBody != null && textDelimiterToSearch != null && cutEmailMessage.TextBody.contains(textDelimiterToSearch)) {
    cutEmailMessage.TextBody = cutEmailMessage.TextBody.substringBefore(textDelimiterToSearch).trim();
  if (cutEmailMessage.HtmlBody != null && htmlDelimiterToSearch != null && cutEmailMessage.HtmlBody.contains(htmlDelimiterToSearch)) {
    cutEmailMessage.HtmlBody = cutEmailMessage.HtmlBody.substringBefore(htmlDelimiterToSearch).trim();

return cutEmailMessage;

What I try to do is to find some predefined text in original email which indicates that a quoted email starts from this point. The getReplyDelimiters method returns lines like "From:" or "-----Original Message-----", etc.
When I run my code in Execute Anonymous on an existing EmailMessage record which has one of these delimiters it returns me what I'm expecting.
Note. A lot of records are processed correctly. I can't figure out the difference.

Edit 1.

On each modification I have an email sent to me to check what message was updated. Also I create a backup record with original HtmlBody and TextBody. Also I surround all my code with

catch(Exception e){

I have no such emails.

Edit 2.

All EmailMessages WITH attached images are not processed properly. But still, even on those EmailMessages TextBody is processed correctly.

  • So your code works on existing records, but does not work with the same content with it's running before insert? Do I have that right? There are some aspects of Task and Email processing that are a bit... magic. Have you tried running it after insert or in a future or Queueable fired by the trigger? – David Reed Dec 4 '18 at 1:43
  • My code works in before insert on EmailMessages without attached images. After insert does not work as you can't perform DML update on EmailMessage record. – Andrii Muzychuk Dec 4 '18 at 8:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.