+ Start a Discussion
Morgan MarcheseMorgan Marchese 

Why Is This Code Throwing a De-Reference Null Object Error?

Hoping someone can help me here, I'm getting a de-reference null object error on some code. This code has been working perfectly up until now so I'm kind of scratching my head on this one, we haven't made any code changes recently to this class. I know that a de-reference means that I am making a call to a null list or variable, and that to combat that you should do checks for .size > 0 before proceeding with any implementation, but, as far as I can tell, we've already done that?

Salesforce reports the error at line 106 which when looking in Salesforce is this line:
 
System.debug('KLE spc per account : '+accToSPCMap.get(account.id).size() + ' for acc: '+account.id);

That line comes directly after a size check:
if(accToSPCMap.size() > 0){

So, I'm making it past the .size() > 0 check, but then still de-referencing on the System.debug line directly below it. I'm having trouble tracking down WHAT the null is at this point. Anybody see anything I missed or have any suggestions on tracking the null better?  

Thanks for your time.
 
public with sharing class SendAlertForRelatedProductPlans
{
    // Make the constructor private since this is a utility class that should not be instantiated
    private SendAlertForRelatedProductPlans() 
    {
    }

    public static void checkNewSubscriptionProductCharges(List<Zuora__SubscriptionProductCharge__c> newSubscriptionProductCharges) 
    {
       // The below code will query for the accounts related to the SPCs that are being inserted
        Set<Id> accountIdSet = new Set<Id>();
        List<Messaging.SingleEmailMessage> messages = new List<Messaging.SingleEmailMessage>();

        for(Zuora__SubscriptionProductCharge__c spc : newSubscriptionProductCharges)
        {
            if(spc.Zuora__Account__c != null && spc.Zuora__Subscription__c != null) // Make sure the SPCs are related to an account and subscription
            {
                accountIdSet.add(spc.Zuora__Account__c);
            }
        }

        if(accountIdSet.size() == 0)
        {
            System.debug('The SPCs being inserted were not associated with accounts, quitting early.');
            return; // If none of the SPCs are related to accounts, quit early 
        }

        List<Account> accounts = [SELECT Id, Name, Customer_Number__c FROM Account WHERE Id IN :accountIdSet];

        // For each account, we'll need to query for *all* the related active subscriptions.
        Map<Id, Zuora__Subscription__c> subscriptions = new Map<Id, Zuora__Subscription__c>([SELECT Id, Name, Zuora__Account__c
                                                                                             FROM Zuora__Subscription__c 
                                                                                             WHERE Zuora__Account__c IN: accountIdSet 
                                                                                             AND Zuora__Status__c = 'Active'
                                                                                             AND Name != null]);

        // If there are no active subscriptions associated with the accounts specified, we should not continue.
        if(subscriptions.size() == 0)
        {
            System.debug('There were no active subscriptions associated with the SPCs being inserted. Quitting early.');
            return;
        }

        Set<Id> subscriptionIdSet = subscriptions.keySet();

        // We also need to query for all SPCs associated with the above active subscriptions.
        // Retrieve any SPCs that are:
        // (a) Associated with the set of subscriptions ids
        // (b) The subscriptions must be active
        // (c) Associated with the set of account ids within this trigger context 
        // (d) The SPC isn't a Discount Record
        // (e) The SPC has a Rate Plan Classification
        Map<Id,Zuora__SubscriptionProductCharge__c> spcsAssociatedWithActiveSubscription = new Map<Id,Zuora__SubscriptionProductCharge__c>(
                                                                                         [SELECT Id, RatePlanClassification__c, Zuora__Account__c, Zuora__Subscription__c
                                                                                          FROM Zuora__SubscriptionProductCharge__c
                                                                                          WHERE Name != 'Discount' 
                                                                                          AND RatePlanClassification__c != null
                                                                                          AND Zuora__Subscription__c IN : subscriptionIdSet
                                                                                          AND Zuora__Subscription__r.Zuora__Status__c = 'Active' 
                                                                                          AND Zuora__Account__c IN :accountIdSet]);

        Map<Id, List<Zuora__SubscriptionProductCharge__c>> accToSPCMap = new Map<Id, List<Zuora__SubscriptionProductCharge__c>> ();
        for(Zuora__SubscriptionProductCharge__c zs: spcsAssociatedWithActiveSubscription.values()){
            List<Zuora__SubscriptionProductCharge__c> sub = accToSPCMap.get(zs.Zuora__Account__c);
            if(sub == null){
                sub = new List<Zuora__SubscriptionProductCharge__c>();
                sub.add(zs);
                accToSPCMap.put(zs.Zuora__Account__c, sub);
            }else{
                sub.add(zs);
            }

        }

        // The below two maps will capture whether a subscription has:
        // (a) at least one 'Support' related SPC 
        // (b) at least one 'Software' related SPC

        Integer count = 0;

        System.debug('KLE accounts : '+accounts.size());

        for(Account account : accounts){
            Set<ID> subscriptionIdHasSupportSPCs = new Set<ID>();
            Set<ID>  subscriptionIdHasSoftwareSPCs = new Set<ID>();
            Set<ID>  subscriptionIdHasCommSPCs = new Set<ID>();

            if(accToSPCMap.size() > 0){
            System.debug('KLE spc per account : '+accToSPCMap.get(account.id).size() + ' for acc: '+account.id);
                for(Zuora__SubscriptionProductCharge__c spc : accToSPCMap.get(account.id))
                {
                    if(spc.RatePlanClassification__c == 'Support')
                    {
                        subscriptionIdHasSupportSPCs.add(spc.Zuora__Subscription__c);
                        
                    }
                    else if(spc.RatePlanClassification__c == 'Software')
                    {
                        subscriptionIdHasSoftwareSPCs.add(spc.Zuora__Subscription__c);
                        
                    }
                    else if(spc.RatePlanClassification__c == 'Communication')
                    {
                        subscriptionIdHasCommSPCs.add(spc.Zuora__Subscription__c);
                        
                    }
                    count++;

                } // End of loop through SPCs
           }

 
Best Answer chosen by Morgan Marchese
Morgan MarcheseMorgan Marchese
Hi Terence,

Thanks for replying again. After this post and my post on StackExchange, we were able to pinpoint (as you said loosely) that our for loop was iterating through a larger list of accounts than the list we had in the map after all of our filter criteria were applied. The for loop wasn't focusing on only accounts in the map, it was doing a broader list of accounts based on the select statement at line 28.

Once I came to that realization, I figured that we ultimately don't care about everything in the account list, we only care about the things that made their way into the map, so I made the following change:
 
if(accToSPCMap.size() > 0){
                        if(accToSPCMap.containsKey(account.id) == true){
                                 // Do Work
                       }
            }


 

All Answers

Terence_ChiuTerence_Chiu
Your accToSPCMap variable might have elements, however, you are printing size of the list stored in the map in the debug log. Perhaps the map does not contain the account id as a key. Within that if statement can you add the following line:

system.debug(accToSPCMap.containsKey(account.id));

If it prints false then that may be your problem as the get method returns null because the key/value pair doesn't exist in the map. You nest another if statement inside to check for that as well.

if(accToSPCMap.containsKey(account.id)){
    System.debug('KLE spc per account : '+accToSPCMap.get(account.id).size() + ' for acc: '+account.id);

}
 
@Karanraj@Karanraj
If there is no matching account.id is found in the Map accToSPCMap and you are trying to check the size() then it will throws the error message. Just remove the size() in the error line and check the value 
accToSPCMap.get(account.id)
Check this link for more details to understand about the error message - http://help.salesforce.com/HTViewSolution?id=000063739
 
Morgan MarcheseMorgan Marchese

Hey Guys,

Many thanks for your answers. I took Terence's suggestion of adding system.debug(accToSPCMap.containsKey(account.id)); after my IF statement, and found that while running through the newly inserted records I get a lot of responses of containsKey: True (which is expected), until finally reaching a containsKey: false which at that point throws the null reference error.

I could use a little more help here if you would be kind enough to provide me with some next troubleshooting steps. It's hard to test this with a single record since these records are inserted automatically via API integration. 

From my understanding of this code, one of the first things we do is check if the SPC records being inserted are linked to SFDC Accounts. IF they are tied to accounts, we add them to the accountIdSet and then we check if accountIdSet is > 0 before we continue. If the record being inserted is not tied to an account, shouldn't it not be making it through the checks since there would be no account ID and therefore they wouldn't be put in the list? 

for(Zuora__SubscriptionProductCharge__c spc : newSubscriptionProductCharges)
        {
            if(spc.Zuora__Account__c != null && spc.Zuora__Subscription__c != null) // Make sure the SPCs are related to an account and subscription
            {
                accountIdSet.add(spc.Zuora__Account__c);
            }
        }

        if(accountIdSet.size() == 0)
        {
            System.debug('The SPCs being inserted were not associated with accounts, quitting early.');
            return; // If none of the SPCs are related to accounts, quit early 
        }
 

I guess my main question is how am I ending up with records in my map that don't have the account.id key? Every record that lands in the map should have an associated account id.

As always, your time and expertise are appreciated!

Terence_ChiuTerence_Chiu
You are applying filters like checking Status == 'Active'. The issue is the list of accounts you have is not included in the list of Zuora__SubscriptionProductCharge__c records in your map. Is is possible that because you are applying filters in your WHERE clauses that you are excluding records of that custom object where you did not exclude when you are storing values in accountIdSet.
Morgan MarcheseMorgan Marchese
Hi Terence,

Thanks for replying again. After this post and my post on StackExchange, we were able to pinpoint (as you said loosely) that our for loop was iterating through a larger list of accounts than the list we had in the map after all of our filter criteria were applied. The for loop wasn't focusing on only accounts in the map, it was doing a broader list of accounts based on the select statement at line 28.

Once I came to that realization, I figured that we ultimately don't care about everything in the account list, we only care about the things that made their way into the map, so I made the following change:
 
if(accToSPCMap.size() > 0){
                        if(accToSPCMap.containsKey(account.id) == true){
                                 // Do Work
                       }
            }


 
This was selected as the best answer