-1

I have a batch and I can't figure out how to fix System.NullPointerException error

going through my debug log my kes variable is null enter image description here

for (Known_Error_Subscription__c kes : scope) {
            kes.Article_Status__c = kavMap.get(kes.Id).Known_Error_Status__c;
            System.debug('***Article_Status__c ' + kes.Article_Status__c);
       
    }

But don't quite understand because before kes is shown as null in the DEBUG log, it seems to have some values

enter image description here

So why all of sudden kes is null?

In the batch, I am trying to perform a field update.

global class BatchArticleEmail implements Schedulable, Database.Batchable<SObject> {

global string query;
Set<Id> kavIds = new Set<Id>(); 

global void execute(SchedulableContext sc) {
    Id batchProcessId = Database.executeBatch(this);
}
global BatchArticleEmail() {
    
    List<Knowledge__kav> kavList = [SELECT KnowledgeArticleId FROM Knowledge__kav];
    for(Knowledge__kav kav : kavList) {
        kavIds.add(kav.KnowledgeArticleId);
    }
    System.debug('BatchArticleEmail kavList ' +kavList);
    
    query = 'Select Id, KnowledgeKA__c, Article_Status__c, Article_Title__c FROM Known_Error_Subscription__c WHERE KnowledgeKA__c IN :kavIds';     
    System.debug('query BatchArticleEmail ' +query);
}

global Database.QueryLocator start(Database.BatchableContext BC){
     return Database.getQueryLocator(Query);
}

global void execute(Database.BatchableContext BC, List<Known_Error_Subscription__c> scope) {
    System.debug('#### Scope BatchArticleEmail ' + scope.size());
    
    Set<Id> kesIds = new Set<Id>();
    List<Known_Error_Subscription__c> kesList = [SELECT KnowledgeKA__c, Article_Status__c, Article_Title__c FROM Known_Error_Subscription__c];
    for(Known_Error_Subscription__c kes : kesList) {
        kesIds.add(kes.KnowledgeKA__c);
        System.debug('BatchArticleEmail kesList Article Status' +kes.Article_Status__c);
    }
    System.debug('BatchArticleEmail kesList KnowledgeArticleId ' +kesIds);
    
    Map<Id, Knowledge__kav> kavMap = new Map<Id, Knowledge__kav>(
        [SELECT KnowledgeArticleId, Title, Known_Error_Status__c FROM Knowledge__kav WHERE KnowledgeArticleId IN :kesIds]);
    System.debug('BatchArticleEmail Knowledge__kav Map ' +kavMap);

    for (Known_Error_Subscription__c kes : scope) {
            kes.Article_Status__c = kavMap.get(kes.Id).Known_Error_Status__c;
            System.debug('***Article_Status__c ' + kes.Article_Status__c);
       
    }
    update scope;   
}
global void finish(Database.BatchableContext info) {
    
}

}
1

The null variable assignment that you found may be a red herring. The system probably assigns null to 'kes' when the function runs, and/or perhaps when the loop or function finishes. There are lots of null assignments that happen behind the scenes to avoid invalid pointer problems that you see in other languages, and may explain what you're seeing in the log.

My guess is that the error is related either to the 'query' property being null, or 'kavIds'. These are two properties defined outside of a function, which means they are "instance" variables. Function variables are created and destroyed every time at function is run. So e.g. 'kesIds' is an example of a function variable, whose lifecycle begins when the a single batch execution begins, and ends when it ends.

Instance variables on the other hand begin when the BatchArticleEmail is instantiated (new BatchArticleEmail() is run). The unusual thing about batch jobs is that the instance is preserved from one execution to another. So the constructor is called once, when the batch begins, and it is never called again, even when a new batch is being run. However, the data for that instance is not preserved, by default. So when the batch job is created, kavIds exists, but the first time the execute method is called, it is operating on null data, because it's being run in a new execution environment, and the data was never stored anywhere. It's kind of like what might happen if you were able to swap hard drives while a program is running in RAM -- it's in the middle of running a program, but the underlying data (disk) has been swapped, and everything is now null. So when it tries to access that data, it starts getting null reference errors.

The typical solution is to make your batch job "stateful". This means that the instance-level data will be serialized into storage, then deserialized when the next execution method is ready to run. It can be more expensive due to the extra processing, which is why they don't do it by default. But for your purposes the serialization should be negligible.

global class BatchArticleEmail implements Schedulable, Database.Batchable<SObject>, Database.Stateful {
    // ...
}

https://www.biswajeetsamal.com/blog/tag/database-stateful/

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.