Try-catch spanning trigger transactions has been exceptionally unreliable for about a year now (if not longer). Some time ago, you used to be able to catch the correct type of exception (e.g. a NullPointerException would be just that), while I noticed either last year or early this year that any exception in a trigger would be converted to a DmlException instead, the original error lost. Now, it appears that any unhandled exception in a trigger immediately terminates the transaction, with the status code for the log showing that exception.
I'm going to check with some people over at salesforce.com and bring this up, see if it's an intentional change, or if there needs to be a bug logged. For now, the best advice I can offer you is this: do not allow exceptions to leave your current code context (e.g. a trigger) unless you're intentionally trying to terminate the transaction. Each code unit should be responsible for handling its own exceptions. If you need to report an error, always use the addError
method to report the error back to the calling context.
This is really a best practice anyways. Exceptions should be exceptional (or ideally, never), and when/if they do happen, they should be handled as close to the source as possible. Always try to write code that won't generate exceptions, particularly NullPointerException, ListException, QueryException, MathException, and TypeException. All of these exceptions are easily avoidable and/or can be handled in the immediate vicinity of where they can occur.
Edit: I built a unit test for this, and the code worked as expected. Here's the trigger and unit test for context:
trigger q232645 on Lead (before insert) {
if(Trigger.new[0].LastName == 'Exception') {
Lead record;
// This line here generates a NullPointerException...
record.LastName = 'Test';
}
}
@isTest class q232645Test {
@isTest static void test() {
try {
insert new lead(LastName='Exception');
System.assert(false, 'Should not reach here; trigger should throw exception.');
} catch(Exception e) {
System.debug('We should see this in the logs.');
return;
}
System.assert(false, 'We should not get this far because of the return statement.');
}
}
This unit test passes, despite the execute anonymous code not working. I'm convinced this is a bug, and I'll do my best to have some one look in to this for you.
Edit 2:
This is intended behavior, according to this help article (thanks, @KiranMachhewar!). You must not allow exceptions to escape the trigger context.