# Avoiding governance limit errors in NetSuite Map/Reduce scripts

> Source: https://netray.co/erp-help/netsuite-map-reduce-script-governance-limits
> Publisher: Netray (netray.co), AI and integration engineering for ERP and manufacturing systems.
> Section: ERP Help

## Short answer

A NetSuite Map/Reduce script gives each map or reduce invocation of a key its own fresh governance allotment instead of sharing one pool across the whole run, so most SSS_USAGE_LIMIT_EXCEEDED failures come from a single key doing too much work, not from the total record count. Fix it by moving heavy logic out of getInputData, keeping map and reduce functions idempotent and cheap per key, and checking runtime.getCurrentScript().getRemainingUsage() before expensive calls.

## Kind

advanced

## System

netsuite

## Erp

Oracle NetSuite

## Module

SuiteScript 2.x / Map/Reduce

## Query

how to avoid governance limit errors in NetSuite Map/Reduce scripts

## Alternate Queries

- netsuite map reduce script stuck in queued status
- netsuite SSS_USAGE_LIMIT_EXCEEDED map reduce
- map/reduce vs scheduled script governance netsuite
- netsuite getRemainingUsage yield script

## Applies To

SuiteScript 2.0 and 2.1 Map/Reduce script type, all NetSuite editions with SuiteCloud enabled

## Steps

### Design a Map/Reduce script that stays inside governance

**Items:**

In getInputData, return a search or query object directly (search.create(...).run(), or query.runSuiteQL) rather than iterating results into an array in the stage itself; NetSuite paginates the input for you across the map stage.

Keep getInputData's own logic light: it has its own governance budget and a hard 60 minute wall-clock limit before it fails with SSS_TIME_LIMIT_EXCEEDED, separate from the per-key budget used later.

In the map stage, do one record's worth of work per key. If a single key needs to touch many related records (e.g. an order with 500 lines), split the work further upstream in getInputData so each key is small.

Call runtime.getCurrentScript().getRemainingUsage() before any record.load, record.save, search.create.run() or N/https call inside map or reduce, and log a warning when it drops below a safety threshold such as 50 units.

Use context.write(key, value) in map to hand data to reduce instead of doing lookups twice; reduce receives an array of values for each key so batch related work there.

In summarize, iterate context.mapSummary.errors and context.reduceSummary.errors (both are N/error.SuiteScriptError maps keyed by input key) and log or email a digest instead of letting failures disappear silently.

Check Customization > Scripting > Map/Reduce Script Status for the deployment to see per-stage progress and key counts; a script stuck in Queued is usually waiting on account concurrency, not failing.

If a stage genuinely needs more than one execution's worth of work per key, redesign around N/task's MapReduceScriptTask to chain a follow-up script rather than trying to force more governance out of one deployment.

## Sections

### Why governance is allocated per key, not per run

**Body:**

A Scheduled Script (SuiteScript 2.x scheduled type) gets one governance pool for the entire execution, so a script processing 10,000 records shares that pool across all of them and commonly needs to reschedule itself with N/runtime and N/task to pick up where it left off.

Map/Reduce works differently on purpose: NetSuite invokes the map function once per key returned by getInputData, and each of those invocations is its own execution with its own governance allotment (1,000 units on standard accounts, higher with a SuiteCloud Plus license). The same applies to each reduce invocation, which receives all values written for a given key.

This means a Map/Reduce script that fails with SSS_USAGE_LIMIT_EXCEEDED is almost never failing because there are too many total keys. It is failing because one key's map or reduce function is doing too much work on its own, for example loading a large record with many sublists, running a heavy saved search, or making several synchronous N/https calls inside a single invocation.

**Code:**

define(['N/search', 'N/runtime'], function(search, runtime) {
  function map(context) {
    var remaining = runtime.getCurrentScript().getRemainingUsage();
    if (remaining < 50) {
      log.audit('Low governance', 'key=' + context.key + ' remaining=' + remaining);
    }
    var data = JSON.parse(context.value);
    // do one record's worth of work here
  }
  return { map: map };
});

### The queued and pending states that are not errors

**Body:**

A Map/Reduce deployment sitting in Queued or Pending status on the Map/Reduce Script Status page is not stuck. NetSuite runs a limited number of Map/Reduce and scheduled script queues concurrently per account, and the concurrency limit depends on the account's provisioning and SuiteCloud Plus license, so deployments genuinely wait their turn behind other scheduled work, workflow actions and other Map/Reduce jobs.

If a deployment is queued for an unusually long time, check Setup > SuiteCloud Development Framework or Customization > Scripting > Script Deployments for other deployments with higher priority ('High' vs 'Medium'), and consider spreading heavy jobs across off-peak hours using the deployment's schedule rather than firing everything at midnight.

### getInputData pitfalls that look like governance errors

**Body:**

Returning a fully materialized array from getInputData (for example .map()'ing over search results into a plain JS array) forces the getInputData stage itself to load and hold every row in memory and against its own governance and time budget, which can fail before map ever starts, with an error that looks unrelated to the eventual data volume.

Prefer returning the search object itself, a query.Query result, or an object of the form { type: 'search', id: 'customsearch_x' }. NetSuite then streams the input to the map stage in its own paginated internal process, which is far more resilient than a hand-rolled array.

### Chaining and self-rescheduling for genuinely large jobs

**Body:**

When the total dataset is large enough that even well-designed keys risk the account's daily Map/Reduce concurrency or the job needs to run in controlled batches, use N/task's MapReduceScriptTask from a Scheduled Script or another Map/Reduce's summarize stage to kick off the next chunk instead of trying to process everything in one deployment invocation.

This also gives you a clean audit trail per run (each task submission is its own execution with its own log) which is much easier to debug than one long-running job whose logs are impossible to isolate by batch.

## Pitfalls

- Iterating search results into an array inside getInputData instead of returning the search or query object directly.
- Assuming a Queued deployment has failed and re-triggering it manually, which creates duplicate concurrent runs once the original clears the queue.
- Doing multiple record.load/record.save round trips per key in map when a single record.submitFields or a Query-based bulk update would use far fewer units.
- Ignoring context.mapSummary.errors and context.reduceSummary.errors in summarize, so individual key failures never surface until someone notices missing data.
- Writing non-idempotent logic in map or reduce (e.g. incrementing a counter record) that produces wrong totals when NetSuite retries a key after a transient failure.
- Scheduling several heavy Map/Reduce deployments at the same trigger time, causing self-inflicted queuing that looks like a platform problem.

## Ai Angle

ERPray can be pointed at a Map/Reduce script's execution log and the account's script deployment records and explain, in plain terms, which stage and which specific key exceeded governance, whether the account was simply queued behind other jobs, and whether the fix is a code change or a scheduling change. For teams maintaining many Map/Reduce customizations, SyteRay-style grounded review of the getInputData return shape is often enough to catch the array-materialization mistake before it ships.

## Faq

### Does SuiteCloud Plus change Map/Reduce governance limits?

**Answer:** Yes. Accounts with a SuiteCloud Plus license get a higher per-invocation unit allotment for the map, reduce and summarize stages than standard accounts, which is why the same script can behave differently across a production account and a sandbox without the add-on provisioned.

### Why does my Map/Reduce script show far fewer keys processed than records in the search?

**Answer:** Check whether getInputData is returning a search with a row limit, whether a saved search has a maximum results setting, or whether the search itself uses a formula filter that is silently excluding rows. The Map/Reduce Script Status page's key count reflects what getInputData actually returned, not the full dataset you expect.

### Can I increase the concurrency limit for Map/Reduce scripts?

**Answer:** Concurrency is tied to account provisioning and license level rather than a deployment setting. If jobs are consistently queued behind each other, the practical fix is to stagger schedules, raise deployment priority for time-sensitive jobs, or consolidate several small Map/Reduce scripts into fewer, better-batched ones.

### Is it safe to call an external REST API from inside the map stage?

**Answer:** It works but each N/https call consumes governance units and adds latency per key, which multiplies across every invocation. Batch external calls where the API supports it, or move the integration to a Scheduled Script that processes accumulated results, rather than calling out once per record inside map.

## Search terms

netsuite map reduce governance limit, SSS_USAGE_LIMIT_EXCEEDED map reduce, netsuite map reduce script status queued, getRemainingUsage suitescript, netsuite getInputData best practice, map reduce vs scheduled script netsuite, netsuite mapreducescripttask, suitescript 2.1 map reduce governance, netsuite summarize stage errors, netsuite script deployment concurrency

---
Talk to an engineer: https://netray.co/contact?intent=erp_ai | Machine catalog: https://netray.co/agent/catalog.json
