When using an Approval - Group activity with the Wait For Condition based on script option, the Approval - Group activity may get stuck and not progress the workflow.

Steps to Reproduce


  1. Import the attached wf_workflow_version_9b2912815343030007d9435723dc348e.xml.

  2. Create a task record with approval rules set for it.

  3. Approve any of the generated user approvals.

    The group approval should be State = Approved, other approvals should No longer required, and the workflow should complete via the Approved path.



  1. Replace the Approval - Group activity definition with the code listed below. Please note the script to be replaced is the only section related to the function _determineOverallState and not the whole script code.
  2. Remove the related Customer Update [sys_update_xml] record, so that this file will receive future updates.

  // Determine the overall state of the approvals (approved, rejected or '')
   _determineOverallState: function() {
      var ids = activity.scratchpad.approval_ids;
      if (!ids)
         return 'skipped';
      var waitFor = activity.vars.wait_for;
      if (waitFor == 'script') {
         if (!activity.vars.approval_script)
            return 'approved';
         // Set up information to be passed to the script
         //    overall counts
         var ret = this.approvalUtils.getGroupIdListApprovalCounts(ids);
         ret.counts['total'] -= ret.counts['not_required'];
         ret.counts['total'] -= ret.counts['duplicate'];
         ret.counts['total'] -= ret.counts['cancelled'];
         workflow.prepareScriptVariable('counts', ret.counts);
         workflow.prepareScriptVariable('approvalIDs', ret.approvalIDs);
         //    group level counts and user ids
         var groups = {};
         var groupRet={};
         for (var i = 0; i < ids.length; i++) {
            var id = ids[i];
            // We now track the approvals in the scratchpad for better
            // detection of duplicate user approvals that were deleted
            if (GlideStringUtil.notNil(activity.scratchpad[id]) && typeof activity.scratchpad[id] !== "undefined")
            groupRet = this.approvalUtils.getGroupUserApprovalCounts(activity.scratchpad[id]);                                                                                                                                                                                                                                                                            
      groupRet = this.approvalUtils.getUserGroupApprovalCounts(id);
            groups[id] = {};
            groups[id].approvalIDs = groupRet.approvalIDs;
            for (var s in groupRet.counts)
               groups[id][s] = groupRet.counts[s];
         workflow.prepareScriptVariable('groups', groups);
         var newState = this.runScript(activity.vars.approval_script);
// If the newState is not requested then it has changed to Approved, Rejected, Cancelled
// or a custom state so mark any remaining Group approvals to No Longer Required
if (newState && GlideStringUtil.notNil(newState) && (newState != 'requested')) {
var groupApprovals = new GlideRecord('sysapproval_group');
groupApprovals.addQuery('sys_id', ids);
while (groupApprovals.next()) {
groupApprovals.approval = newState;
this.approvalUtils.setPendingUserApprovalsByGroup(groupApprovals.sys_id, 'not_required');
if (!newState || GlideStringUtil.nil(newState))
newState = 'requested';
         return newState;
      // check for approval state change within each group
      var rejectHandling = activity.vars.reject_handling;
      var changed = false;
      var groupApprovals = new GlideRecord('sysapproval_group');
      groupApprovals.addQuery('sys_id', ids);
      while (groupApprovals.next()) {
         if (this._determineGroupState(groupApprovals, waitFor, rejectHandling))
            changed = true;
      if (!changed && !this.forceState)
         return '';
      // And then the overall state
      var ret = this.approvalUtils.getGroupIdListApprovalCounts(ids);
      ret.counts['total'] -= ret.counts['not_required'];
      ret.counts['total'] -= ret.counts['duplicate'];
      ret.counts['total'] -= ret.counts['cancelled'];
      if (ret.counts['total'] == 0)
         return 'skipped';
      if ((ret.counts['rejected'] > 0) && (rejectHandling == 'reject'))
         return 'rejected';
      if ((waitFor == 'any' || waitFor == 'first_any') && (ret.counts['approved'] > 0))
         return 'approved';
      if ((waitFor == 'first_any') && (ret.counts['rejected'] > 0))
         return 'rejected';
      if ((waitFor == 'all' || waitFor == 'any_each' || waitFor == 'first_each') && (ret.counts['approved'] == ret.counts['total']))
         return 'approved';
      if ((ret.counts['rejected'] + ret.counts['approved']) == ret.counts['total']) {
         // we are complete - we have to make a decision
         return 'rejected';
      return 'requested';



Related Problem: PRB1244065

Seen In

There is no data to report.

Intended Fix Version


Fixed In

Jakarta Patch 8
Kingston Patch 3

Safe Harbor Statement

This "Intended Fix Version" information is meant to outline ServiceNow's general product direction and should not be relied upon in making a purchasing decision. The information provided here is for information purposes only and may not be incorporated into any contract. It is not a commitment, promise, or legal obligation to deliver any material, code, or functionality. The development, release, and timing of any features or functionality described for our products remains at ServiceNow's sole discretion.

Associated Community Threads

There is no data to report.

Article Information

Last Updated:2018-03-19 22:03:38