+ Start a Discussion
John AthitakisJohn Athitakis 

...caused by: System.StringException: Invalid id: undefined

So I have a strange error. I have a bit of code that is updating leads and adding them as members to campaigns when a field is updated from an external tool with a campign ID. It works fine in my testing, but in the last day have been getting the following error when the external tool is touching the record. From what I can see, the field in quesiton is not being accessed:

 

LeadCreateCampaignMember: execution of AfterUpdate
caused by: System.StringException: Invalid id: undefined
Trigger.LeadCreateCampaignMember: line 24, column 1

I will bold line 24 in this case:


/* 
Author: John Athitakis
Synopsis: When a new or existing Lead has a special string field populated, an event is created with that string as its subject line
*/

trigger LeadCreateCampaignMember on Lead (after insert, after update) {
   //  Logic: On insert if the Event Activity Field is Populated, Create an Event.
 if(Trigger.IsInsert) {
 for (Lead UpdatedLead: Trigger.new) {
     if(UpdatedLead.campaign_id__c!=null){ 
            CampaignMember cml = new CampaignMember();
            cml.campaignid = UpdatedLead.campaign_id__c;
            cml.leadid = UpdatedLead.id;
            Database.upsert(cml, false) ;
     }
 }
 }
   // Logic: On update, if there is a change in the Event Activity Field & it is not Null, Create an Event 
 if(Trigger.IsUpdate){
     for (Lead UpdatedLead2: Trigger.new) {
      Lead PriorLead = Trigger.oldMap.get(UpdatedLead2.ID);
         if (UpdatedLead2.campaign_id__c != PriorLead.campaign_id__c && UpdatedLead2.campaign_id__c!=null){
          CampaignMember cml2 = new CampaignMember();
          cml2.campaignid = UpdatedLead2.campaign_id__c;
          cml2.leadid = UpdatedLead2.id;
          Database.upsert(cml2, false);
}
     }}}
 Obviously

Best Answer chosen by John Athitakis
Roy LuoRoy Luo
I would guess the external tool was feeding bad data for campaign_id__c. You could use CampaignMember keyPrefix to check the incoming campaign id.

Also always bulkify db access.
String prefix = CampaignMember.sObjectType.getDescribe().keyPrefix;

List<CampaignMember> cmItems = new List<CampaignMember>();

for (Lead UpdatedLead2: Trigger.new) 
{
      Lead PriorLead = Trigger.oldMap.get(UpdatedLead2.ID);
      if (UpdatedLead2.campaign_id__c != PriorLead.campaign_id__c && UpdatedLead2.campaign_id__c!=null && UpdatedLead2.campaign_id__c.startsWith(prefix))
     {
          CampaignMember cml2 = new CampaignMember();
          cml2.campaignid = UpdatedLead2.campaign_id__c;
          cml2.leadid = UpdatedLead2.id;
          cmItems.add(cml2);
     }
}
Database.update(cmItems, false);



 

All Answers

Roy LuoRoy Luo
I would guess the external tool was feeding bad data for campaign_id__c. You could use CampaignMember keyPrefix to check the incoming campaign id.

Also always bulkify db access.
String prefix = CampaignMember.sObjectType.getDescribe().keyPrefix;

List<CampaignMember> cmItems = new List<CampaignMember>();

for (Lead UpdatedLead2: Trigger.new) 
{
      Lead PriorLead = Trigger.oldMap.get(UpdatedLead2.ID);
      if (UpdatedLead2.campaign_id__c != PriorLead.campaign_id__c && UpdatedLead2.campaign_id__c!=null && UpdatedLead2.campaign_id__c.startsWith(prefix))
     {
          CampaignMember cml2 = new CampaignMember();
          cml2.campaignid = UpdatedLead2.campaign_id__c;
          cml2.leadid = UpdatedLead2.id;
          cmItems.add(cml2);
     }
}
Database.update(cmItems, false);



 
This was selected as the best answer
John AthitakisJohn Athitakis
Thanks---having some follow up troubles. Using your code (as follows) I get no errors but nothing is being inserted anymore. What did I miss? I have a condition for insert and update since with an insert looking at the old map was throwing errors.
trigger LeadCreateCampaignMember on Lead (after insert, after update) { 
String prefix = CampaignMember.sObjectType.getDescribe().keyPrefix;
List<CampaignMember> cmItems = new List<CampaignMember>();

for (Lead UpdatedLead2: Trigger.new) 
{
      
    if(Trigger.IsInsert){
    if (UpdatedLead2.campaign_id__c!=null && UpdatedLead2.campaign_id__c.startsWith(prefix))
     {
          CampaignMember cml2 = new CampaignMember();
          cml2.campaignid = UpdatedLead2.campaign_id__c;
          cml2.leadid = UpdatedLead2.id;
          cmItems.add(cml2);
     }    
    }  
    if(Trigger.IsUpdate){
      Lead PriorLead = Trigger.oldMap.get(UpdatedLead2.ID);
      if (UpdatedLead2.campaign_id__c != PriorLead.campaign_id__c && UpdatedLead2.campaign_id__c!=null && UpdatedLead2.campaign_id__c.startsWith(prefix))
     {
          CampaignMember cml2 = new CampaignMember();
          cml2.campaignid = UpdatedLead2.campaign_id__c;
          cml2.leadid = UpdatedLead2.id;
          cmItems.add(cml2);
        
     }}
     Database.upsert(cmItems, false);
}
  
}

Also--and I apologize for this, my old test class had 100% coverage. Changing over I drop down significantly. How does one adapt a test class to be better for bulkification? this is my current test class (was 100%).
 
@isTest 
private class LeadCreateCampaignMemberTest {
    static testMethod void validateLeadCreateCampaignMember() {
//Logic: Create a lead. A short coming here to be corrected later is the declarative for OwnerId
        Lead lead = new Lead();
        lead.LastName = 'last';
        lead.FirstName = 'first';
        lead.Company = 'Company';
        lead.Status = 'Unqualified';
        lead.IsUnreadByOwner = True;
        lead.OwnerId='005G0000001Dabm';
        lead.campaign_id__c = '701G0000000Az8r';  
        test.startTest();
        insert lead;    
//Logic: Find the event that was created as part of the lead being created
        CampaignMember cmTestInsert = [SELECT Id, campaignid, leadid FROM CampaignMember WHERE leadid = :lead.Id AND campaignid = :lead.campaign_id__c];
//Logic: Date Time is set to an always true value. Setting it to NOW or similar. Other comparisons are per the Trigger Code        
        system.assertEquals(lead.id, cmTestInsert.leadid);
        system.assertEquals(lead.campaign_id__c, cmTestInsert.campaignid);
//Logic: Check the later half of the trigger--on Update.
        lead.campaign_id__c = '701G0000000BN8Z';        
        update lead;
        CampaignMember cmTestUpdate = [SELECT Id, campaignid, leadid FROM CampaignMember WHERE leadid=:lead.Id AND campaignid=:lead.campaign_id__c];
        system.assertEquals(lead.id, cmTestUpdate.leadid);
        system.assertEquals(lead.campaign_id__c, cmTestUpdate.campaignid);
        test.stopTest();  
        
  
    }
}

 
John AthitakisJohn Athitakis

Figured out part of it. I havnt used prefix before and since I was comparing against an id I simply changed this and the code is now firing

 

String prefix = '701';

I'm a bit lost on the test class though; went from 100% to 51% -.- 

Roy LuoRoy Luo
The prefix should be of Compaign, not CompaignMember

String prefix = Campaign.sObjectType.getDescribe().keyPrefix;
John AthitakisJohn Athitakis
Thank you Roy; Campaign works (and is probably better than saying '701' since i could get some other junk).

Still a tad lost on the test class. Trying to look for other bulkified test class examples but not noticing what I might be missing. 
John AthitakisJohn Athitakis
Never mind: suddenly my code is at 100% with the same unaltered test classes. Huh!
Roy LuoRoy Luo

Move Database.upsert(...) outside the for loop.

Do NOT do db access in loop. Queue them up and hit db only one shot and that is a good 'bulkify' example.
 
trigger LeadCreateCampaignMember on Lead (after insert, after update) 
{ 
  String prefix = CampaignMember.sObjectType.getDescribe().keyPrefix;
  List<CampaignMember> cmItems = new List<CampaignMember>();

  for (Lead UpdatedLead2: Trigger.new) 
  {
        
      if(Trigger.IsInsert)
      {
        if (UpdatedLead2.campaign_id__c!=null && UpdatedLead2.campaign_id__c.startsWith(prefix))
         {
              CampaignMember cml2 = new CampaignMember();
              cml2.campaignid = UpdatedLead2.campaign_id__c;
              cml2.leadid = UpdatedLead2.id;
              cmItems.add(cml2);
         }    
      }  
      if(Trigger.IsUpdate)
      {
          Lead PriorLead = Trigger.oldMap.get(UpdatedLead2.ID);
          if (UpdatedLead2.campaign_id__c != PriorLead.campaign_id__c && UpdatedLead2.campaign_id__c!=null && UpdatedLead2.campaign_id__c.startsWith(prefix))
          {
              CampaignMember cml2 = new CampaignMember();
              cml2.campaignid = UpdatedLead2.campaign_id__c;
              cml2.leadid = UpdatedLead2.id;
              cmItems.add(cml2);
            
         }
      }
  }
  Database.upsert(cmItems, false);
  
}

 
Roy LuoRoy Luo
Please mark this sovled to move it out of my tracking stack. Thanks.
John AthitakisJohn Athitakis
Thank you! Will do:)