🔹 Introduction
Two critical skills for NetSuite developers are:
- Error Handling → How to catch, log, and handle errors in scripts without breaking user workflows or losing data.
- Governance → NetSuite enforces usage units (governance limits) on API calls. Scripts must manage these limits to avoid abrupt termination.
🔹 Part 1: Error Handling
Why It Matters
- Errors stop your script unless handled.
- Logging errors helps debugging.
- Users/admins need clear messages (not just “An unexpected error occurred”).
Example 1: Simple Try–Catch
/**
*@NApiVersion 2.1
*@NScriptType UserEventScript
*/
define([], () => {
const beforeSubmit = (context) => {
try {
// Example: required field check
const rec = context.newRecord;
const email = rec.getValue('email');
if (!email) {
throw new Error('Email is required before saving this record.');
}
log.debug('Validation Passed', 'Email present.');
} catch (e) {
// Log the error for debugging
log.error('Validation Error', e.message);
// Rethrow to stop the save
throw e;
}
};
return { beforeSubmit };
});
💡 Explanation:
try
runs risky code.catch (e)
logs error details.throw e
stops record save with a readable error.
Example 2: Catch & Continue
/**
*@NApiVersion 2.1
*@NScriptType ScheduledScript
*/
define(['N/search'], (search) => {
const execute = () => {
try {
const results = search.create({
type: search.Type.CUSTOMER,
filters: [['isinactive', 'is', 'F']],
columns: ['entityid']
}).run().getRange({ start: 0, end: 10 });
results.forEach(result => {
try {
const name = result.getValue('entityid');
if (!name) throw new Error('Missing customer name');
log.debug('Customer', name);
} catch (innerErr) {
// Handle per-record errors without failing whole script
log.error('Customer Error', innerErr.message);
}
});
} catch (e) {
log.error('Script Error', e.message);
}
};
return { execute };
});
💡 Explanation:
- Wrap per-record operations in an inner
try–catch
. - Keeps script running even if one record fails.
Example 3: Logging Stack Traces
log.error({
title: 'Error in map stage',
details: JSON.stringify(e) // Logs stack trace, name, and message
});
💡 Always log more than just e.message
→ use JSON.stringify(e)
for debugging.
🔹 Part 2: Governance
What is Governance?
NetSuite limits how much work a script can do by assigning usage units to API calls.
Examples (approximate):
record.load()
→ 10 unitsrecord.save()
→ 20 unitssearch.run()
→ 10 unitsrecord.submitFields()
→ 4 units
If a script runs out of units → SSS_USAGE_LIMIT_EXCEEDED error.
Checking Remaining Usage
/**
*@NApiVersion 2.1
*@NScriptType ScheduledScript
*/
define(['N/runtime'], (runtime) => {
const execute = () => {
try {
const script = runtime.getCurrentScript();
log.debug('Remaining Usage', script.getRemainingUsage());
} catch (e) {
log.error('Error checking usage', e.message);
}
};
return { execute };
});
Example 1: Stop Early if Low Usage
if (runtime.getCurrentScript().getRemainingUsage() < 100) {
log.debug('Governance', 'Stopping early to avoid usage exceeded.');
return; // exit script cleanly
}
Example 2: Rescheduling (Scheduled Script)
/**
*@NApiVersion 2.1
*@NScriptType ScheduledScript
*/
define(['N/runtime', 'N/task'], (runtime, task) => {
const execute = () => {
try {
const script = runtime.getCurrentScript();
if (script.getRemainingUsage() < 200) {
log.debug('Governance', 'Low usage, rescheduling script.');
task.create({
taskType: task.TaskType.SCHEDULED_SCRIPT,
scriptId: script.id,
deploymentId: script.deploymentId
}).submit();
return;
}
// Continue processing logic here
} catch (e) {
log.error('Error in governance check', e.message);
}
};
return { execute };
});
💡 Explanation:
- Rescheduling lets the script resume later with a fresh usage quota.
- Important for long-running Scheduled Scripts.
Example 3: Use Map/Reduce for Heavy Loads
Instead of manually rescheduling, use a Map/Reduce script → NetSuite automatically yields and resumes as needed.
🔹 Best Practices for Error Handling & Governance
- Always wrap risky code in try–catch.
- Use per-record try–catch to keep batch jobs running.
- Log errors with context (record ID, field, etc.).
- Use runtime.getCurrentScript().getRemainingUsage() frequently.
- Prefer submitFields() over full
load + save
for small updates. - Break work into chunks → process in batches of 100 or less.
- For big data jobs, use Map/Reduce (built-in resumption).
✅ Key Takeaway
- Error Handling → Prevents script crashes, improves debugging.
- Governance Management → Ensures scripts finish cleanly without exceeding limits.
- Together, they make your SuiteScripts robust, scalable, and production-ready.