I read through some of the existing posts on here about this error and I think I'm still confused on how to solve it in my context.

I am trying to prevent duplicate products from being added to an opportunity record. I am using the product Id as means to check, which may be an issue in itself between sandbox and production, but for now that is what I went with.

I am getting this error: "sObject row does not allow errors". I am not operating on old records (trigger.old).

My goal is to display a visual error message on the add product screen letting the user know they can't add 2 products with the same product Id

Question: What syntax do I need to modify to avoid this error?

Trigger code below:

trigger OpportunityProductTrigger on OpportunityLineItem (before insert, after insert, before update, after update) {

      List<OpportunityLineItem> oppProductList = new List<OpportunityLineItem>();

      // insert context
      if(Trigger.isInsert) {

        if(Trigger.isBefore) {

          for (OpportunityLineItem ol : Trigger.New) {

        } // close isAfter

Class method that gets called from the trigger code:

public static void checkDuplicateProducts(List<OpportunityLineItem> oppProductList) {
    Set<Id> oppIds = new Set<Id>();
    Set<Id> newProductIds = new Set<Id>();

    // get the opportunity ids and product codes passed in
    for(OpportunityLineItem ol : oppProductList) {

    List<OpportunityLineItem> existingProducts = [SELECT Id, Name, ProductCode,  Product2Id FROM OpportunityLineItem WHERE OpportunityId IN : OppIds];

    for( OpportunityLineItem ol : existingProducts) {

      if( newProductIds.contains(ol.Product2Id) ) {
        ol.addError('You cannot add duplicate products');


1 Answer 1


You're trying to modify the record you queried, not the record in the trigger. You can't add an error to a record that's not in Trigger.old or Trigger.new.

Your code also isn't bulkified; multiple line items from multiple opportunities may be falsely flagged as duplicates.

Here's a better implementation:

// Track products by opportunity Id, then by Product2Id
Map<Id, Map<Id, OpportunityLineItem>> productsByOpportunity = new Map<Id, Map<Id, OpportunityLineItem>>();
// Only look for matching products
Set<Id> productIds = new Set<Id>();
for(OpportunityLineItem record: oppProductList) {
    productsByOpportunity.put(record.OpportunityId, new Map<Id, OpportunityLineItem>());
// Check for duplicates in the current transaction
for(OpportunityLineItem record: oppOpportunityList) {
    OpportunityLineItem currentDuplicate = productsByOpportunity.get(record.OpportunityId).put(record.Product2Id, record);
    if(currentDuplicate != null) {
        currentDuplicate.addError('This is a duplicate product for this opportunity.');
// Check for other duplicate product line items
for(OpportunityLineItem record: [
        SELECT Product2Id, OpportunityId 
        FROM OpportunityLineItem 
        WHERE OpportunityId = :productsByOpportunity.keySet() AND Product2Id = :productIds
]) {
    OpportunityLineItem currentDuplicate = productsByOpportunity.get(record.OpportunityId).get(record.Product2Id);
    if(currentDuplicate != null) {
        currentDuplicate.addError('This is a duplicate product for this opportunity.');

You must log in to answer this question.

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