Adding Contacts to Campaign as Campaign Members

An upsert allows the DML operation to choose between an "insert" and an "update" operation. If a record already exists, it updates it. If the record is new, or doesn't already exist, it inserts it.

In your situation, if the record already exists and there are no changes to it, then you don't need to update it. You only need to insert new records. I don't see a check in your trigger above to see if the contact is already a member of the campaign. It appears to me that once you've assembled the external Id's, you need to query to see if any of them exist and remove them from your list.

First exception on row 0; first error: INVALID_FIELD_FOR_INSERT_UPDATE, Unable to create/update fields: ContactId. Please check the security settings of this
field and verify that it is read/write for your profile or permission set.: [ContactId]: Trigger.contactTrigger: line 67, column 1

The above tells you the problem begins someplace within line 67 of your code and relates to the ContactId.

Since I can't tell if you've posted all of your code or what the line numbers are, based on what you've posted, I'm guessing the problem lies with this line:

CampaignMember cm= New CampaignMember(CampaignId=UTMCampaign.Id, ContactId= con.Id, Status='Opt-In',External_ID__c = ExternalCampaingID); 

It appears to me the error is telling you that the contact Id already exists in your database for that External_ID__c.


Not 100% why (I think because CampaignMember can point to Contact and Lead) you can't include ContactId in any update call for the CampaignMember object, even if you aren't actually updating the field.

So I think for this scenario, assuming you do need to update the existing members, you would need to identify the contacts already on the campaign, and do that as a separate update call...you are already pulling the Campaigns, so maybe you can also pull all campaign members for the contacts in the trigger for the unique list of campaigns, then make a set of CampaignId + ContactId, and use a contains to check if they exist. If they do, add them to an update call, it not create them.