Recently somebody asked me some questions about when to use addError, and it is true that there is always an aura of mystery about when and how to use this Salesforce method. My idea in this blog post is try to explain addError uses with practical examples, in order that it is demystified.
If you talk to a Java developer about how to do error handling on triggers, his first thought will be: it is easy, throw an exception! But what happens in Salesforce when you throw an exception on a trigger? Let’s say we have a trigger on Account:
trigger AccountTrigger on Account (after update) { for (Account acct: Trigger.new) { throw new MyException('Exception thrown on accoutn trigger'); } }
If we try to update an existing account on a standard page, we will get this ugly error:
In addition, if the trigger is processing several records, because a bulk DML operation has been performed, the execution will be stopped once the first error is found. This is, if I have 100 different errors on my updated accounts, I will have to save 100 times to detect the 100 errors. We could build an exception handling system in which we accumulate the error messages and throw a single exception once the 100 records have been validated, however this doesn’t solve the ugly error message issue.
Then addError comes to the rescue! In Salesforce you can use addError method over an sObject or over an sObject field. If you use it in trigger context:
- If the operation that is being performed is a bulk DML operation (insert/update/delete a collection of things), first every record will be processed and marked with or without error, and then the full operation will be rolled back.
- If the operation is a single DML operation, and the record is marked with error, it will be rolled back too.
- If you are on a standard page, saving a record, errors added with addError will be shown on your page (or on the fields you added them), in a nice standard way.
Let’s see what happens if we use addError in our account trigger:
trigger AccountTrigger on Account (after update) { for (Account acct: Trigger.new) { acct.Name.addError('Error added to Name on trigger'); acct.addError('Error added to account on trigger'); } }
The errors are automatically added to the standard page, and are shown in a nice way. The same nice error messages will be shown if you try to save the record over a Salesforce API.
Let’s talk now about visualforce pages. What happens in a Visualforce page if I am using exceptions on a trigger? Let’s create a visualforce page for editing accounts:
<apex:page controller="AccountController"> <apex:pageMessages/> <apex:form > <apex:pageBlock title="Editar nombre de cuenta para el usuario: {!$User.FirstName}"> <apex:pageBlockSection > <apex:pageBlockSectionItem >Nombre de la cuenta: <apex:inputField value="{!account.Name}"/></apex:pageBlockSectionItem> <apex:pageBlockSectionItem >Número de la cuenta: <apex:inputField value="{!account.AccountNumber}"/></apex:pageBlockSectionItem> </apex:pageBlockSection> <apex:pageBlockButtons ><apex:commandButton action="{!save}" value="save"/></apex:pageBlockButtons> </apex:pageBlock> </apex:form> </apex:page>
public with sharing class AccountController { private final Account account; public AccountController() { account = [SELECT Id, Name, AccountNumber FROM Account][0]; } public Account getAccount() { return account; } public PageReference save() { update account; return null; } }
Let’s assume that the first trigger that we created, the one that throws the exception is active. Then what would happen if I try to update an account in this visualforce page?
The exception has not be handled, so an ugly message appears. What some people do is capture the exception and add the exception message to the page:
public with sharing class AccountController { private final Account account; public AccountController() { account = [SELECT Id, Name, AccountNumber FROM Account][0]; } public Account getAccount() { return account; } public PageReference save() { try { update account; } catch (Exception e) { ApexPages.addMessages(e); } return null; } }
Note that there are several ways of adding error messages to the page, here we are using one of the simplest, but you can refer to this great post to explore all the options.
Then, the next happens:
This is, we have been able of printing a better message, but it is still ugly!
However, what happens if I use addError in the trigger? Assume that now we are using the second account trigger I wrote, the one that uses addError.
Let’s try first to not catch the exception on the controller:
This is, if you are on a visualforce controller, performing a DML operation, you have to catch the DML block, because if you don’t do it, an unhandled DML exception will occur in consequence of you using addError on the trigger.
What would happen then if we catch the exception and do nothing else?
public with sharing class AccountController { private final Account account; public AccountController() { account = [SELECT Id, Name, AccountNumber FROM Account][0]; } public Account getAccount() { return account; } public PageReference save() { try { update account; } catch (Exception e) {} return null; } }
If you catch the exception, errors will be shown on the page in a nice standard way:
Empty catch blocks are ugly though. What would happen if I would had tried to add the exception messages to the page using ApexPages.addMessages(e); as we did before? Well, as the messages are already on the page, they won’t be double added. You can learn more about this behaviour in this post.
You can use the catch block to add a custom message too, that will be added in addition to the messages added by addError.
ApexPages.addMessage(new ApexPages.Message(ApexPages.severity.ERROR, 'Error added with ApexPages.addMessages'));
In conclusion, if you use addError in triggers, and you are performing DML exceptions from a visualforce page, the errors will be added to your visualforce page in a nice way, however you have to catch the DML operation in order an unhandled exception is not shown.
Finally, I want to highlight that addError can be used outside trigger context too. According to the documentation: “When used in Visualforce controllers, the generated message is added to the collection of errors for the page“.
Let’s see an example of this:
public with sharing class AccountController { private final Account account; public AccountController() { account = [SELECT Id, Name, AccountNumber FROM Account][0]; } public Account getAccount() { return account; } public PageReference save() { try { account.AccountNumber.addError('Error added to AccountNumber on controller'); account.addError('Error added to account on controller'); update account; } catch (Exception e) { ApexPages.addMessage(new ApexPages.Message(ApexPages.severity.ERROR, 'Error added with ApexPages.addMessages')); } return null; } }
In conclusion, if you are using addError outside trigger context:
- Errors added with addError will be shown on your visualforce page (or on the fields you added them) in a nice standard way. In this case, using addError is just equivalent to using ApexPages.addMessage(…), that draws the error in your visualforce page in a nice standard way too. However that way you can’t add messages to fields and using addError you can.
- You are adding errors to a record outside a DML operation context, so neither the addError method or you have to worry about the rollback.
Finally I want to ask you, how do you know that a record has errors added with addError? The only way to know it is checking if the visualforce page has messages. Check this post to learn how to do it.
Here you have a summary of what we have seen:
SUMMARY | addError used in trigger | exception thrown in trigger |
Salesforce API | Nice msg | Ugly msg |
Standard Page | Nice msg | Ugly msg |
DML on VF page, exception not caught | Unhandled exception with nice msg | Unhandled exception with ugly msg |
DML on VF page, exception caught, but nothing done in the catch block | Nice msg | No msg |
DML on VF page, exception caught, ApexPages.addMessages(e) in the catch block | Nice msg (not duplicated) | Ugly msg |
DML on VF page, exception caught, ApexPages.addMessage(custom msg) in the catch block | Nice msg + custom msg | Custom msg |
Thanks again, good explanation
LikeLike
Hi Alba,
This article is quite good to explain addError().
But i have question here, lets suppose i have written multiple addError() in single class(TriggerHelper), multiple addError() in different if statement or inside different for loop. So in the same transaction after hitting 1st addError() statement, will the code continue its execution or it will stop.
For example,
1st addError() statement hit at line 10 inside if statement, and after if() i have 200 more lines of code, so will the code execute for after if() or it will stop after hitting 1st addError()?
Thanks in advance!
LikeLike
It will continue its execution. If you threw an exception, it didn’t, but as you are using addError, it will continue till the end. Hope that helps.
LikeLike