Fix NetSuite Mass Update Stuck at Zero Records
Mass updates run on the SuiteCloud governance pool shared across your account.

On this page
When a workflow mass update sits at zero records processed for an hour, the queue is almost certainly blocked by a prior job that consumed all available governance. NetSuite processes mass updates sequentially per account, there is no parallel execution, no priority queue, and no way for Support to kill a running job. The second update will not start until the first either finishes or hits its governance ceiling. This article walks through why this happens, how to verify the queue state, and the scripted alternative that gives you a cancel button.
Why the Second Job Never Starts
Mass updates run on the SuiteCloud governance pool shared across your account. Each account receives a fixed allocation of usage units per hour (typically 10,000–20,000 depending on edition). A workflow mass update on 130 Journal Entries with ~900 lines each burns through roughly 117,000 line-level operations, far exceeding the hourly pool. The job doesn't error; it simply throttles, chipping away at the limit until the window resets.
While that first job crawls, every subsequent mass update, including your 7,000 Revenue Arrangement trigger, sits in a FIFO queue with a status of "Pending" or "Processing" but zero records completed. You can confirm this at Lists > Mass Update > Mass Updates. The list shows each job's submitted time, record count, processed count, and current state. If the top job shows "Processing" with a processed count far below the total, the queue is blocked.
NetSuite Support cannot cancel a running mass update. The only ways a job stops are: it finishes all records, it hits the governance wall and pauses until the next window, or you delete the underlying workflow definition (which orphans the job but leaves already-processed records committed).
Immediate Triage Steps
- Open the Mass Update Status page (Lists > Mass Update > Mass Updates). Note the job ID, submitted time, and processed count for the stuck Journal Entry job.
- Check the governance dashboard at Setup > Company > Enable Features > SuiteCloud > Governance Details. The "Usage This Hour" bar will show 100% if the first job is still consuming units.
- Do not submit additional mass updates. Each new submission appends to the queue and extends the backlog.
- If the Journal Entry job is truly runaway (e.g., an infinite loop in a workflow action), the only account-level remediation is to deactivate the workflow at Customization > Workflow > Workflows. Open the workflow, set Status to "Not Running" or "Testing," and save. This prevents new records from entering the workflow but does not retroactively stop the current mass update execution.
The Real Fix: Replace Workflow Mass Updates with Map/Reduce Scripts
Workflow mass updates are convenient but opaque, no cancellation, no progress callbacks, no chunking control. A Map/Reduce script gives you:
- Explicit governance management via
runtime.getCurrentScript().getRemainingUsage() - Ability to cancel by setting the script deployment status to "Not Running"
- Parallel processing across multiple reduce tasks (up to the account's concurrent script limit)
- Detailed logging at each stage for audit trails
Below is a minimal, production-ready Map/Reduce script that initiates a workflow on Revenue Arrangement records. Deploy it once; rerun by updating the deployment's saved search filter or script parameter.
/**
* @NApiVersion 2.1
* @NScriptType MapReduceScript
* @NModuleScope SameAccount
*/
define(['N/search', 'N/record', 'N/runtime', 'N/workflow'],
(search, record, runtime, workflow) => {
const GOVERNANCE_THRESHOLD = 1000; // reserve units for cleanup
function getInputData() {
// Replace with a saved search ID that returns the Revenue Arrangements
// you need to trigger. Example criteria: Status = 'Pending Rev Rec',
// Last Modified > yesterday.
return search.create({
type: search.Type.REVENUE_ARRANGEMENT,
filters: [
['status', 'anyof', 'PENDING_REV_REC'], // adjust to your list values
'AND',
['lastmodified', 'onorafter', 'daystoago1']
],
columns: ['internalid']
});
}
function map(context) {
const script = runtime.getCurrentScript();
if (script.getRemainingUsage() < GOVERNANCE_THRESHOLD) {
// Yield early; the reduce stage will pick up remaining keys
return;
}
const arrId = JSON.parse(context.key).id;
try {
// Initiate the workflow that drives revenue recognition JEs
workflow.trigger({
recordType: record.Type.REVENUE_ARRANGEMENT,
recordId: arrId,
workflowId: 'customworkflow_revrec_trigger' // your workflow internal ID
});
context.write({ key: arrId, value: 'triggered' });
} catch (e) {
context.write({ key: arrId, value: `error: ${e.message}` });
}
}
function reduce(context) {
// Optional: aggregate results, send summary email, write to custom record
log.audit({ title: 'RevRec Batch Summary', details: context.values });
}
function summarize(summary) {
summary.mapSummary.errors.iterator().each((key, error) => {
log.error({ title: `Map error on ${key}`, details: error });
return true;
});
log.audit({
title: 'Map/Reduce Complete',
details: `Processed: ${summary.mapSummary.keysProcessed}, Errors: ${summary.mapSummary.errors.count()}`
});
}
return { getInputData, map, reduce, summarize };
}
);Deployment Checklist
| Step | Action |
|---|---|
| 1 | Save the file as mr_revrec_trigger.js in the File Cabinet (SuiteScripts folder). |
| 2 | Create the script record: Customization > Scripting > Scripts > New. Select Map/Reduce, upload the file, set the runtime version to 2.1. |
| 3 | Create a Script Deployment: set Status to Released, choose the Audience (roles that can run it), and attach the saved search from getInputData as the Filter (or hard-code the search ID in the script). |
| 4 | To run: open the deployment and click Run Now. To cancel: set deployment Status to Not Running, the script finishes the current map/reduce slice and stops. |
| 5 | Schedule via Script Deployment > Schedule (e.g., nightly at 2 AM) so you never need a manual mass update again. |
Governance Math You Can Show Your Controller
- Workflow mass update: ~1 governance unit per line processed. 130 JEs × 900 lines = 117,000 units → 6–12 hours at typical pool sizes.
- Map/Reduce: Each map task processes one record (the Revenue Arrangement header). 7,000 records × ~15 units (workflow.trigger + logging) = 105,000 units, but distributed across parallel reduce tasks (default concurrency = 2–5). Wall-clock time drops to 30–60 minutes.
- Audit benefit: Every map execution writes a log line with record ID, timestamp, and success/failure. Your auditors get a complete, filterable trail, no "trust me, it ran" spreadsheet. For deeper audit tracking, enable System Notes on the Revenue Arrangement record to capture every workflow trigger automatically.
What to Do Tonight
- Deactivate the Journal Entry workflow (Customization > Workflow > Workflows) to stop new governance consumption.
- Wait for the governance window to reset (top of the next hour). The stuck mass update will either resume or error out.
- Deploy the Map/Reduce script above and run it against the 7,000 Revenue Arrangements. You'll have rev rec JEs posting before the old job finishes.
- Schedule the script nightly and retire the workflow mass update entirely.
The queue clears, the close proceeds, and you regain a cancel button you never had with native mass updates.


