5

There was a similar question asked here, but it wasn't really answered.

I've got a function that sends an e-mail for the User from a Visualforce Page, pretty simple:

Visualforce Page

<apex:pageMessages id="emailresponse" />
<apex:form >
  <div class="form-group">
    <apex:outputLabel for="to" value="To:" />
    <apex:inputText id="to" styleClass="form-control" value="{!to}" />
  </div>
  <div class="form-group">
    <apex:outputLabel for="subject" value="Subject:" />
    <apex:inputText id="subject" styleClass="form-control" value="{!subject}" />
  </div>
  <div class="form-group">
    <apex:outputLabel for="message" value="Message:" />
    <apex:inputTextarea id="message" styleClass="form-control" value="{!message}" />
  </div>
  <apex:commandButton styleClass="btn btn-default" value="Send" action="{!sendEmail}" reRender="emailresponse" />
</apex:form>

This just takes in 3 parameters: to, subject and message and calls the function sendEmail in the Controller.

With all of the variable assignments, this is what the Controller looks like:

Controller:

private List<String> toList {
  get;
  set;
}

public String to {
  get;
  set;
}

private void createToList(String to) {
  toList = new List<String>();

  this.toList = to.split('\\;');
}

public String subject {
  get;
  set;
}

public String message {
  get;
  set;
}

public List<Messaging.SingleEmailMessage> messageList {
  get;
  set;
}

public PageReference sendEmail() {
  User usr = [SELECT Id, FirstName, LastName, Name, Title, Email, Phone FROM User WHERE Id = :UserInfo.getUserId()];

  createToList(to);

  messageList = new List<Messaging.SingleEmailMessage>();
  messageList = EmailHelper.createPersonalMessage(toList, subject, message, usr, false);

  try {
    Messaging.SendEmailResult[] r = Messaging.sendEmail(messageList);

    if (r.get(0).isSuccess()) {
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.CONFIRM, messageList.size() + ' messages sent.'));
    }
  }
  catch(Exception e) {
    ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'There was a problem sending your messages.'));
    System.debug('@@@@@ Error: ' + e);
  }

  return null;
}

As you can see this is pretty simple, it's just assigning the variables (albeit splitting e-mail addresses if they're comma-seperated), calling a function in EmailHelper called CreateMessage to create a list of single e-mail messages and trying to send it!

Finally, the Helper class which puts it all together:

Helper Class

public static List<Messaging.SingleEmailMessage> createPersonalMessage(List<String> toList, String subjectString, String messageString, User fromUser, Boolean doRemoveUnsubscribed) {
  List<Messaging.SingleEmailMessage> messageList = new List<Messaging.SingleEmailMessage>(); // Create the new message list

  String footer = buildFooter(fromUser); // Build the footer

  Map<String, Id> mapEmailToContactId = new Map<String, Id>(); // Create a new map for the TargetObjectId

  for (Contact c : [SELECT Id, Email FROM Contact WHERE Email IN :toList]) { // Loop through the list, mapping the e-mail address to the contact
    mapEmailToContactId.put(c.Email, c.Id); // Add them
  }

  for (String to : toList) { // Loop through the Call List
    Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage(); // Create a new message

    message.setToAddresses(new List<String>{
      to
    }); // Set the to address...
    message.setSubject(subjectString); // ... Add the subject...
    message.setHtmlBody(messageString + footer); // ... The body plus the footer we built earlier...

    if (mapEmailToContactId.containsKey(to)) {
      message.setTargetObjectId(mapEmailToContactId.get(to)); // ... The target object (if it exists)...
    }

    messageList.add(message); // ... Add it!
  }

  messageList = removeDuplicates(messageList); // Remove duplicates

  if (doRemoveUnsubscribed) { // Do we remove Unsubscribed people?
    messageList = removeUnsubscribed(messageList); // If so, remove them
  }

  return messageList; // Send the kust back
}

public static String buildFooter(User fromUser) {
  return '<br><br><b><span style="color:#767171; font-family:arial,sans-serif">' + fromUser.FirstName + ' ' + fromUser.LastName + '</span>'; // This gets ridiculously long, but it's creating a footer for the e-mails
}

As mentioned in the title, I'm getting this error:

Not Serializable: com/salesforce/api/fast/List$$lcom/salesforce/api/Messaging/SingleEmailMessage$$r

I've tried Exchanging Messaging.SendEmailResult[] r = Messaging.sendEmail(messageList); for just Messaging.sendEmail(messageList); but still get the crash, sending e-mails with and without multiple address, comma-seperated. They work whether I enter 1 or a dozen thinking it maybe something to do with one of those two things.

I'm assuming $$r is somehow in reference to the Controller line: Messaging.SendEmailResult[] r, what's odd is that the e-mails do actually send.

So, what is causing this error and what does the error actually mean?

  • At what point in the control flow do you get the error? – Adrian Larson Nov 23 '16 at 17:04
  • @AdrianLarson I'm not sure. I'm getting the e-mail, so I can assume Messaging.SendEmailResult[] r = Messaging.sendEmail(messageList); is working and that the helper class is doing it's business at least. I'll add some debugging in to see if I can trace the problem. – Dan Jones Nov 23 '16 at 17:10
5

The only reason Salesforce is trying to serialize the List<Messaging.SingleEmailMessage> property is because you are adding it to the View State. You don't need to, however, as you do not reference it in your markup. Simply remove the messageList property and scope it locally. If you really want to keep it as a property, mark it as transient.

Repro

The MVR is simple here:

Page

<apex:page controller="Demo">
    <apex:form />
</apex:page>

Controller

public with sharing class Demo
{
    public List<Messaging.SingleEmailMessage> messages { get; private set; }
    public Demo()
    {
        messages = new List<Messaging.SingleEmailMessage>
        {
            new Messaging.SingleEmailMessage()
        };
    }
}

Fix

Marking messages as transient resolves the error, as does removing it as a property and scoping it locally to the method where it is used.

Transient

public with sharing class Demo
{
    public transient List<Messaging.SingleEmailMessage> messages { get; private set; }
    public Demo()
    {
        messages = new List<Messaging.SingleEmailMessage>
        {
            new Messaging.SingleEmailMessage()
        };
    }
}

Locally scoped

public with sharing class Demo
{
    public Demo()
    {
        List<Messaging.SingleEmailMessage> messages = new List<Messaging.SingleEmailMessage>
        {
            new Messaging.SingleEmailMessage()
        };
    }
}
  • 1
    Thanks Adrian, worked like a charm as answers normally do from yourself! I really need to look into transient and viewstates as I'm woefully under-informed on them. – Dan Jones Nov 23 '16 at 17:21
  • Incidentally, I hadn't realised until you said I wasn't referencing public List<Messaging.SingleEmailMessage> messageList, so marked it as private also. Without knowing more about this, I'm not sure if this helps with this particular problem, but if nothing else it'll make a bit more "secure". – Dan Jones Nov 23 '16 at 17:23
  • 2
    You don't really need to add it as a controller property at all, though. You don't seem to reference it anywhere outside your sendEmail method. – Adrian Larson Nov 23 '16 at 17:24
  • 1
    You're right actually, I'm not. Thanks for that. I'm not entirely sure why I made it a controller property to begin with! – Dan Jones Nov 23 '16 at 17:29
  • N.B. this error can also occur if the non-transient property references objects in other classes that in turn reference the Messaging class. A recent example for me was referencing a variable of type fflib_ISobjectUnitOfWork from the Trailhead UnitOfWork pattern that in turn has deep within its concrete class a reference to the Message class. – cropredy Nov 17 '18 at 1:13

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.