Avoiding Validation Threshold Errors By Conditionally Assigning Values

When you write scripts for validation rules that modify the values of fields in the current object, you must be aware of how this affects the object's so-called "validation cycle".

Before allowing an object to be saved to the database, the application development framework ensures that its data passes all validation rules. The act of successfully running all defined validation rules results in the object's being marked as valid and allows the object to be saved along with all other valid objects that have been modified in the current transaction. If as part of executing a validation rule your script modifies the value of a field, this marks the object "dirty" again. This results in ADF's subjecting the object again to all of the defined validation rules to ensure that your new changes do not result in an invalid object. If the act of re-validating the object runs your scripts that modify the field values again, this process could result in a cycle that would appear to be an infinite loop. ADF avoids this possibility by imposing a limit of 10 validation cycles on any given object. If after 10 attempts at running all the rules the object still has not been able to be successfully validated due to the object's being continually modified by its validation logic, ADF will throw an exception complaining that you have exceeded the validation threshold:

Validation threshold limit reached. Invalid Entities still in cache

A simple way to avoid this from happening is to test the value of the field your script is about to assign and ensure that you perform the field assignment (or setAttribute() call to modify its value) only if the value you intend to assign is different from its current value. An example script employing this approach would look like this:

// Object-level validation rule on a PurchaseOrder object
// to derive the default purchasing rep based on a custom
// algorithm defined in an object function named 
// determinePurchasingRep() if both the Discount and NetDaysToPay
// fields have changed in the current transaction.
if (isAttributeChanged('Discount') && 
    isAttributeChanged('NetDaysToPay')) {
  def defaultRep = determinePurchasingRep()
  // If new defaultRep is not the current rep, assign it
  if (PurchasingRep != defaultRep) {
    PurchasingRep = defaultRep
  }
}
return true
Note: This example illustrates how to avoid a typical problem that can occur when using a validation rule to perform field derivations. The recommended trigger to use for such purposes would be the field-level "After Value Changed" trigger, or alternatively the "Before Insert" and/or "Before Update" trigger. It is still a good practice to perform conditional field assignment in those cases, too. See Deriving Values of a Field When Other Fields Change Value for more information on deriving field values.