Messaging.sendEmail called inside a loop
ID |
apex.correctness.email_messages_in_loop |
Severity |
high |
Remediation Complexity |
medium |
Remediation Risk |
low |
Remediation Effort |
medium |
Resource |
Performance |
Language |
Apex |
Rationale
A transaction may call sendEmail ten times, not ten thousand: the eleventh call throws System.LimitException: Too many Email Invocations. Sending one message per iteration therefore works for the first ten records and aborts the transaction on the eleventh, after ten emails have already left the org and any DML in the same transaction has been rolled back - so the recipients received a notification about a change that was then undone. The limit is per transaction, so the same loop passes every test that uses three records.
The following code illustrates the pattern detected by this rule:
public class Notifier {
public void notifyEach(List<Contact> contacts) {
for (Contact c : contacts) {
// FLAGGED: Messaging.sendEmail called inside a loop
Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{ build(c) });
}
}
public void notifyIndexed(List<Contact> contacts) {
for (Integer i = 0; i < contacts.size(); i++) {
// FLAGGED: Messaging.sendEmail called inside a loop
Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{ build(contacts[i]) });
}
}
public void notifyAll(List<Contact> contacts) {
List<Messaging.SingleEmailMessage> messages = new List<Messaging.SingleEmailMessage>();
for (Contact c : contacts) {
messages.add(build(c));
}
Messaging.sendEmail(messages);
}
public void notifyOne(Contact c) {
Messaging.sendEmail(new List<Messaging.SingleEmailMessage>{ build(c) });
}
private Messaging.SingleEmailMessage build(Contact c) {
Messaging.SingleEmailMessage m = new Messaging.SingleEmailMessage();
m.setTargetObjectId(c.Id);
return m;
}
}
Remediation
Build the messages in the loop and send them once after it: Messaging.sendEmail(messages); accepts up to 100 in a single call, which also counts as one invocation. Where the volume is larger than one transaction can hold, move the send into a batch and let each chunk send its own collection.