How to resolve Record is read-only error in trigger?

Question:
I am writing a trigger to populate the competitors’ names into a custom field on Opportunity. However, while testing, I am getting the following error message:
System.FinalException: Record is read-only: Trigger.updateCompetitors: line 24
Here’s the trigger code I’ve written:
trigger updateCompetitors on Opportunity (after insert, after update) {
List<Opportunity> OppIds = new List<Opportunity>();
for (Opportunity opp : trigger.new) {
OppIds.add(opp);
}
List<OpportunityCompetitor> oppComps = [SELECT Id, CompetitorName, Strengths, Weaknesses FROM OpportunityCompetitor WHERE OpportunityId IN :Trigger.newMap.keySet()];
List<Opportunity> OppstoUpdate = new List<Opportunity>();
for (Opportunity opp : OppIds) {
String allcomps = null;
if (oppComps.size() > 0) {
for (Integer i = 0; i < oppComps.size(); i++) {
allcomps = allcomps + oppComps[i].CompetitorName + ',';
}
}
opp.OpportunityCompetitors__c = allcomps;
OppstoUpdate.add(opp);
}
update OppstoUpdate;
}
I am encountering the error on line 24, which is where I am trying to update the Opportunity records. Can you please check the code and let me know what is wrong?
Answer:
The error System.FinalException: Record is read-only
occurs because you are trying to modify the Trigger.new
records within an after insert
or after update
trigger. In the after
trigger context, the records in Trigger.new
are read-only, which means you cannot modify the fields of the records directly.
CRS Info Solutions provides top-tier Salesforce training in New York, USA, with hands-on projects, expert certification support, and interview preparation to make you job-ready. Gain practical experience and skills to excel in your Salesforce career!!
To resolve this, you should update the Opportunity records in a separate list (not directly modifying Trigger.new
), and then perform the update outside the trigger context.
trigger updateCompetitors on Opportunity (after insert, after update) {
if (Trigger.isAfter) {
if (Trigger.isUpdate || Trigger.isInsert) {
// Step 1: Create set of Opportunity IDs based on updated Opportunities
Set<String> OppIds = new Set<String>();
for (Opportunity opp : Trigger.new) {
OppIds.add(opp.Id);
}
// Step 2: Query the related OpportunityCompetitor records
Map<String, OpportunityCompetitor> matchingOppComps = new Map<String, OpportunityCompetitor>();
for (OpportunityCompetitor oppComp : [SELECT Id, CompetitorName, Strengths, Weaknesses FROM OpportunityCompetitor WHERE OpportunityId IN :OppIds]) {
// Add logic to build a string of competitors
matchingOppComps.put(oppComp.OpportunityId, oppComp);
}
// Step 3: Update Opportunity records outside of Trigger.new
List<Opportunity> OppstoUpdate = new List<Opportunity>();
for (Opportunity theOpp : Trigger.new) {
if (matchingOppComps.containsKey(theOpp.Id)) {
// Found the matching data, now update the Opportunity field
theOpp.OpportunityCompetitors__c = matchingOppComps.get(theOpp.Id).CompetitorName;
OppstoUpdate.add(theOpp);
}
}
// Perform the update in bulk
update OppstoUpdate;
}
}
}
Code explanation:
The trigger updateCompetitors
runs after an Opportunity is inserted or updated. It first collects all the Opportunity IDs from Trigger.new
and queries the related OpportunityCompetitor
records using those IDs. A map is created to store the matching competitors for each Opportunity. Then, it updates the OpportunityCompetitors__c
field on each Opportunity using the fetched competitor data, and finally performs a bulk update of all modified Opportunity records outside of Trigger.new
to avoid read-only errors.
This solution resolves the error and adheres to best practices by keeping the data handling efficient and within Salesforce governor limits.
Summing Up:
To resolve the “Record is read-only” error in a trigger, it’s crucial to understand that in after
triggers, records in Trigger.new
are immutable, meaning they cannot be directly modified. To fix this, you should avoid altering Trigger.new
and instead create a separate list of records to update, perform the necessary changes, and then issue a bulk update outside the trigger context. This approach ensures that you comply with Salesforce’s governor limits and proper trigger execution, preventing the read-only error while maintaining efficient data processing.
Empower Your Career with Salesforce Training in New York
Elevate your professional journey with CRS Info Solutions’ top-rated Salesforce training, crafted to provide the skills and expertise required to excel in the ever-evolving Salesforce ecosystem. Our industry-focused courses span Salesforce Admin, Developer, and AI modules, blending in-depth theoretical knowledge with hands-on experience through practical, real-world projects. Whether you’re new to Salesforce or a seasoned professional, our well-structured program ensures you master the tools and techniques needed to stand out.
With a strong emphasis on practical application, Salesforce training in New York, USA we offer personalized mentorship, detailed study resources, and expert-led certification preparation to fully equip you for success in interviews and beyond. Gain the confidence and skills to thrive in your Salesforce career.
Join our free demo class today and take the first step toward a rewarding future! Enroll now for a free demo!!!