2

I have a trigger on a parent object that manages master detail child records. This trigger needs to check a given flag field on the parent record to determine whether it needs to process the child records. Once it has processed the child records the parent object needs to be updated to clear the flag.

Because the flag needs to be cleared, this processing needs to happen in before update in the parent trigger.

This works fine until I add a rollup summary field on the parent using data from the master detail child objects. When I add this rollup summary field and then set the flag I find that the transaction is aborted with a message of the form:

[null: Object is currently in trigger Xyz, therefore it cannot recursively update itself]

Why does this happen and how can I fix it?

1 Answer 1

2

Even though you cannot set these field values yourself, Salesforce stores rollup summary field values in the database (unlike formula fields which are always in-memory only and are calculated on demand). These values need to be recomputed when one or more of the relevant master detail children changes.

The problem here is the phase at which you update the “child” records. You are updating them in the “parent” trigger “before” phase. This previous Q&A suggests that what you are doing is OK, if not ideal, though the trailhead documentation recommends that related records are updated in the after phase:

There are two types of triggers.

  • Before triggers are used to update or validate record values before they’re saved to the database.
  • After triggers are used to access field values that are set by the system (such as a record's Id or LastModifiedDate field), and to affect changes in other records. The records that fire the after trigger are read-only.

(My emphasis)

By including the update of master detail child records in the before phase, you cause the Salesforce infrastructure to attempt to re-update the parent record to store the re-computed rollup summary value. However, the infrastructure effectively attempts to perform a new DML (sadly it isn't smart enough to notice the record is already being processed in a DML) and you therefore get this recursive update error, much as is warned about in the trailhead documentation:

Note

The system saves the records that fired the before trigger after the trigger finishes execution. You can modify the records in the trigger without explicitly calling a DML insert or update operation. If you perform DML statements on those records, you get an error.

(Again, my emphasis)

To resolve this issue you will need to:

  1. Continue to reset the “flag” in the before phase.
  2. Update the master detail “child” records in the after phase.

Shifting the latter to the after phase side-steps the problem because at that point Salesforce allows a new DML operation (as is effectively being used by the infrastructure to update the rollup summary field) to be started against the same record (though not using the same in-memory SObject from the Trigger context, since the latter records are set read-only).

The added complexity is in ensuring that the after phase still knows which "parent" record(s) needed to have their master detail "children" updated – you’ve already cleared the “flag” in the before phase so you can’t use that field’s value from the after phase.

You may still be able to determine this from the Trigger.old data. However, if not, you could use a static Set<Id> to hold the IDs of the "parent" records that had the “flag” cleared in the before, and use this set to find the same "parent" record(s) again in the after phase. Note that the content of such a static variable persists for the entire transaction scope (not just the trigger invocation) so remember to clear the entries out once you’ve dealt with these "parent" record(s) in the after phase. Remember too that triggers can be called in chunks of up to 200 records, so you will not be able to clean out this set in one go but must do so in chunks (removing the entries using theSet.removeAll(Trigger.newMap.keySet())).

NB: The set of IDs approach would appear not to be workable if this “flag” could be set during “parent” record creation, since there is no Id field value in the before phase. However, this is not a problem because it would be impossible for there to be any master detail children at this point since it is the Id that defines the relationship from “child” to “parent”. Just be clean and avoid adding null to this set.

You must log in to answer this question.

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