I have created a stateful Apex Batch Job which is processing Permission Sets defined by a Set of Ids. I want to process each Permission Set individually so the batch size is 1.

The batch job creates the correct number of batches and completes successfully, however not all Permission Sets retrieved in the "start" method get processed.

By debugging and logging processed Ids I can see that a certain number of Permission Sets actually never enter the "execute" method. Instead some Permission Set records get processed twice.

here is my code:

public class AnalyzePermissionSets implements Database.Batchable<SObject>, Database.Stateful {

private Set<Id> setIds;

//set to debug processed Permission Sets
private Set<Id> processedSets;

public AnalyzePermissionSets(Set<Id> incomingIds) {
    this.setIds = incomingIds;

    this.processedSets = new Set<Id>();

public Database.QueryLocator start(Database.BatchableContext BC) {
    return Database.getQueryLocator([SELECT Id, Name, NameSpacePrefix, (SELECT Id FROM ObjectPerms)
                                     FROM PermissionSet 
                                     WHERE Id IN :setIds]);

public void execute(Database.BatchableContext BC, List<PermissionSet> scope) {
    PermissionSet pSet = scope.get(0);

    // Within this method i am manipulating the ObjectPermissions,
    //FieldPermissions and SetupEntityAccessItems of the Permission Set

    // logging Id to see which Sets actually have been processed

public void finish(Database.BatchableContext BC) {

An example:

  1. created 1 Batch job with 8 Batches total
  2. batch job completes sucessfully
  3. setIds.size() = 8 and processedSets.size() = 4

I found a kind of similar question here: Batch apex failling silently for some records

But I tested this in a Sandbox and Developer Edition and both times almost the exact same number of Permission Sets did not get processed (difference was one set). The Permission Sets are mostly part of a external managed Package but also custom.

Any ideas how to solve this? Let me know if you need further information.

  • 3
    One thing that may help: don't do the sub-query in the initial query. This can have unpredictable behavior. Instead, query the children records in the execute method based on the scope.
    – sfdcfox
    May 4, 2017 at 15:02
  • @sfdcfox I tried that before but whenever i do this i see an interesting behaviour: if you look in the console in the column "Operation": Instead of the usual value "Batch Apex" I see "SerialBatchApexRangeChunkHandler". additionally I see some batches failing with "First error: Attempt to de-reference a null object". May 4, 2017 at 15:10
  • I forgot to mention. I am not using the sub-query in the code...I always query the children separately. May 4, 2017 at 15:18
  • SerialBatchApexRangeChunkHandler is normal. It's how salesforce manages sub-batches. The null pointer error is something else entirely.
    – sfdcfox
    May 4, 2017 at 15:18
  • @sfdcfox I thought so. The null pointer is caused because in the "execute" method the scope contains completely different Permission Sets which were not even part of the batch job in the first place. I don't understand how just by putting in the sub-query (and not even using it) the behavior of the batch job changes. In both cases the batch scope does NOT contain the correct records. May 4, 2017 at 15:34

1 Answer 1


I figured out what was the issue:

PermissionSet Object seem not to work as expected in batch jobs. Using Permission Sets the scope given to the execute method would contain rather random Permission Sets:

ATTEMPT 1: Using Sub-Query in Query Locator

Database.getQueryLocator([SELECT Id, Name, NameSpacePrefix, (SELECT Id FROM ObjectPerms) FROM PermissionSet WHERE Id IN :setIds]);

The content in the Iterator of the QueryLocator Object was incorrect. Some Permission Sets were present two or more times in the iterator. The execute block processed all Permission Sets in the iterator but because of duplicates, Permission Sets got processed twice.

ATTEMPT 2: No Sub-Query

Database.getQueryLocator([SELECT Id, Name, NameSpacePrefix FROM PermissionSet WHERE Id IN :setIds]);

The content in the iterator was correct but in the execute block scope some Permission Sets got processed which were never part of the iterator.

In both cases the number of batches was always correct.

My Solution was to create a Custom Object which stores exactly one Permission Set Id and loop through a collection of those records instead of the Permission Set records, querying the Permission Set in each execute block.

  • I would have expected that to be mentioned in some documentation though instead of having to trial-and-error my way to that knowledge. May 5, 2017 at 12:59
  • I'm not sure if you know this, but PermissionSet is a type of SObject. Where did you get the impression that this object is not supported? Do you have documentation somewhere you can cite to back up that claim?
    – Adrian Larson
    May 5, 2017 at 13:03
  • Well to my understanding PermissionSet is not an SObject. It is no instance of SObject Apex-Object and has also not the methods which are possible to access on SObjects. As to proof: I learned that by debugging QueryLocator (and Iterator) in the start and execute method blocks and comparing the content. Based on my code above I tried two things: May 5, 2017 at 13:17
  • That understanding is completely incorrect. You can prove it to yourself quite simply with a script like: SObject record = new PermissionSet();. Or query for it if you prefer. But it is a type of SObject.
    – Adrian Larson
    May 5, 2017 at 13:20
  • @AdrianLarson Apologies you are right: Permission Set is indeed an SObject. Still the observed behavior is concerning since by using a Custom Object and querying Permission Sets in the execute block worked completely fine. It seemed to be the same Permission Sets which didn't get processed (managed and custom) so I don't know what could be the reason for this. I will adjust my answer above. May 5, 2017 at 13:27

You must log in to answer this question.

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