I have a simple trigger which is meant to block a record from being deleted based on the record type.

I am using a Trigger Handler by Kevin O'Hara


trigger InvoiceTrigger on Invoice__c (before delete) {
    new InvoiceTriggerHandler().run();    

Trigger Handler

public class InvoiceTriggerHandler extends TriggerHandler {

    public InvoiceTriggerHandler() {}

    public override void beforeDelete(){

        for(Invoice__c invoice : (List<Invoice__c>) Trigger.new) {

            if (IsLocked(invoice)){
                invoice.addError('You cannot delete an Invoice record which is locked. Please contact your Salesforce Administrator.');

    private boolean IsLocked(Invoice__c invoice){
        return invoice.RecordTypeId == InvoiceUtils.getLockedRecordTypeId;

Trigger Unit Test

public class InvoiceTriggerTests {

    public static void InvoiceTrigger_Success(){

        // arrange
        Id invoiceId = TestDataFactory.createInvoice();

        // act

        // assert
        System.assert(true); // don't judge me :)

But when I run the unit test I get this error message:

Error Message

System.DmlException: Delete failed. First exception on row 0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, InvoiceTrigger: execution of BeforeDelete

caused by: System.NullPointerException: Attempt to de-reference a null object

Class.InvoiceTriggerHandler.beforeDelete: line 7, column 1 Class.TriggerHandler.run: line 43, column 1 Trigger.InvoiceTrigger: line 2, column 1: []


  1. What is actually causing the problem?
  2. How can I fix it?

1 Answer 1


In the delete context, your trigger.new will be null. Use trigger.old instead for delete events.

  • even with before delete ?
    – Robs
    Jul 29, 2018 at 15:08
  • 1
    Always. There are no new records.
    – Adrian Larson
    Jul 29, 2018 at 15:09
  • They haven't been deleted yet either? so the syntax is a little wonky...
    – Robs
    Jul 29, 2018 at 15:10
  • 1
    @Robs "before" means "before initial commit", while "after" means "after initial commit" (see the Triggers and Order of Execution docs). Trigger.old doesn't exist in insert/undelete operations (because there was no previous state), and Trigger.new does not exist in delete (because after deletion, there is nothing left to commit). It's perfectly logical once you understand the above documentation.
    – sfdcfox
    Jul 29, 2018 at 17:52
  • 1
    @Robs old and new were probably a bad choice of words, but we're stuck with what we have. Think of "old" as "beforeDatabaseCommits" and "new" as "afterDatabaseCommits", which is really what they mean in this context. It's not wonky, in that sense, because in a "beforeDatabaseCommits" context, the records exist, but in the "afterDatabaseCommits" context, they don't (they've been deleted!).
    – sfdcfox
    Jul 29, 2018 at 20:27

You must log in to answer this question.

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