I am trying to prevent duplicate Lead records from being created when users import Lead records into Salesforce that have the same email address.

Here is the scenerio:

  1. A user imports leads using the Data Import Wizard
  2. If the Lead being imported already exist in Salesforce the Lead from the spreadsheet will not be inserted into salesforce. And the existing record that was found should have an activity added to it.

I have a Lead trigger below that either:

  • Adds a task to the existing Lead but allows the duplicate to be created
  • Or the duplicate will fail on insert but the task is never added to the existing Lead

The issue is that I am unable to stop duplicates from being imported into Salesforce and then creating a task on the existing Lead record that was found during the import process.

The trigger I have is below.

  trigger LeadPreventDuplicate on Lead (after insert,after update) {

Map<String, Lead> leadMap = new Map<String, Lead>();

  for (Lead lead : System.Trigger.new) 

   if ((lead.Email != null) && (System.Trigger.isInsert ||
        (lead.Email != System.Trigger.oldMap.get(lead.Id).Email))) 
          leadMap.put(lead.Email, lead);

  List<task> addtask=New List<task>();

  for (Lead lead : [SELECT Email FROM Lead WHERE Email IN :leadMap.KeySet()]) 
    Lead newLead = leadMap.get(lead.Email);  // task being added to new Leads but should only be for existing leads
    addtask.add(new Task(
    WhoID =lead.id, 
    Status = 'Active',
    Subject = 'Test Task',
    ActivityDate = system.today()));

   insert addtask; 

   Lead[] dupes = new Lead[0];
      Set<String> email = new Set<String>(), dupEmail = new Set<String>();
          for(Lead record: Trigger.new)
          for(Lead record: [SELECT Id, Email FROM Lead WHERE Email IN :email])
          for(Lead record: Trigger.new)
delete dupes;
  • 3
    Hi Mike, what is the specific problem with the code you've shared here? Have you considered using Salesforce native duplicate management instead of code?
    – David Reed
    Commented Jan 17, 2019 at 16:37
  • 1
    I second that you should strongly consider out-of-the-box Duplicate Management.
    – Adrian Larson
    Commented Jan 17, 2019 at 16:42
  • Also just to note, you should not act on records other than trigger.new in any before context. Inserting new records should go in an after trigger.
    – Adrian Larson
    Commented Jan 17, 2019 at 16:47
  • I did look at the duplication management functionality but I need to be able to not only prevent a duplicate from being inserted but I need to add a task to the existing Lead. For example an existing Lead record(1) has the email address [email protected]. A user has a csv that has a record(2) where the email address is equal to [email protected]. The import needs to fail and an activity record needs to be added to lead record(1). Are you suggested I incorporate the Duplicate Management OTB functionality and a trigger? Thanks for the replies.
    – Mike
    Commented Jan 17, 2019 at 16:49
  • Created Matching and Duplicate rule to 'Block' on creation and edit. Non Duplicate Leads went in and the Duplicates leads did not. This is good except the task are never created on the existing Leads. Tried using Data Import Wizard within Salesforce using create and Add New and Update existing Records but the activities never showed on the existing Leads.
    – Mike
    Commented Jan 17, 2019 at 19:12

1 Answer 1


A user imports leads using the Data Import Wizard

If the Lead being imported already exist in Salesforce the Lead from the spreadsheet will not be inserted into salesforce. And the existing record that was found should have an activity added to it.

These things are rather tricky to do together. As you have probably found, using addError()

    newLead.Email.addError('A lead with this email address already exists!');

does block the Lead from being added, but it also results in a rollback of the transaction, and that rollback takes your new Task out with it. (I'm glossing over niceties of allOrNone=false here because you, as the trigger, cannot control whether you're running in an allOrNone context or not).

I think the simplest way to achieve this is to move your trigger operation to after insert and then delete the duplicate Leads, rather than trying to block them from being inserted in the first place. While there is a way to do the former, it'd involve bouncing the Ids of the "master" Leads into a different transaction via Platform Events to do the Task insertion (because posting a Platform Event is non-transactional), while using addError() to block the Leads from being inserted.

Taking the deletion approach: You can't perform DML directly on Trigger.new while in after insert context (the sObjects are read-only), but you can still delete them. This is legal, for example, if very weird:

if (Trigger.isAfter && Trigger.isInsert) {
    List<Lead> leads = new List<Lead>();
    for (Lead a : Trigger.new) {
        leads.add(new Lead(Id = a.Id));
    delete leads;

Hence, if you switch to using after insert context, you can preserve your Task addition and your deletion of duplicate Leads in a single trigger.

I still don't really like this architecture. I think it's non-idiomatic and likely to cause behavior that, while correct, is unintuitive. But it's something you can do if you must.

  • David, thank for the suggestion. I assumed that the Delete will happen after the task insertion. Also, how would this process be cleaner? Should the task insert and deletion be in separate triggers?
    – Mike
    Commented Jan 18, 2019 at 17:22
  • The cleanest solution is to use out-of-the-box duplicate management. I don't know where the add-a-Task requirement stems from so I can't comment on that, but if it's negotiable, I would push back.
    – David Reed
    Commented Jan 18, 2019 at 17:24
  • We capture Customer engagements as Activities on the Lead record. The lead record just houses the customer basic information while the activities record what the customer has done. I updated the code that I am testing with in the original comment. Activities are created to existing Lead but the New Leads did not get inserted. I am assuming that this is because I changed the trigger to after insert/update.
    – Mike
    Commented Jan 18, 2019 at 17:34
  • That's because you had your trigger delete all of the new leads, rather than just the duplicates.
    – David Reed
    Commented Jan 18, 2019 at 17:36
  • Ok I see that now. But how do I find the inserted duplicates? My initial query is to select the email addresses that are in the Lead object and compare them to the the values in the keyset. I do not want to delete the values that are not in the keyset because those are the new non duplicate leads. So do I need to query salesforce again to find newly created lead that have a matching email address?
    – Mike
    Commented Jan 18, 2019 at 20:26

You must log in to answer this question.

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