SuiteScript 1.0 to 2.1: Where an AI Conversion Runs Clean and Is Still Wrong
A converted script that saves the record and loses a line
The Sales Order saves. Nothing appears in the execution log, no error banner, no failed deployment. The total is short by one line, and it has been short on every order since the converted script went live.
This is the SuiteScript 1.0 the conversion started from:
var count = nlapiGetLineItemCount('item');
for (var i = 1; i <= count; i++) {
var qty = nlapiGetLineItemValue('item', 'quantity', i);
total += parseFloat(qty);
}
And this is a conversion where every API mapping is correct:
const count = rec.getLineCount({ sublistId: 'item' });
for (let i = 1; i <= count; i++) {
const qty = rec.getSublistValue({
sublistId: 'item',
fieldId: 'quantity',
line: i
});
total += parseFloat(qty);
}
nlapiGetLineItemCount does become getLineCount. nlapiGetLineItemValue does become getSublistValue. The positional arguments do become an options object. The loop bounds were left alone, and that is the entire defect: line indexing begins at 0 in SuiteScript 2.x, so the rewritten loop skips the first line and asks for one line past the last.[1]
The fix is two characters wide.
for (let i = 0; i < count; i++) {
// body unchanged
}
A reviewer reading the diff sees a correctly modernized loop. A test that saves one order with one line passes. The script only misbehaves on records with more than one line, which is every real order.
Oracle shipped the rule list, not the automation
Oracle has published netsuite-suitescript-upgrade as part of SuiteCloud Agent Skills, a package of instructions and reference data that loads into coding assistants such as Codex, Claude Code, and Cline.[2][3]
The reference material is the part that matters for 1.0 work. The skill carries more than 125 API mappings across 26 modules, 34 object conversions with 331 method mappings, 13 APIs with no direct equivalent, and 16 categories of breaking behavioral change, of which 1-based to 0-based indexing is the first one listed.[4]
That list is the useful asset, and it is worth more than the conversion mode attached to it. A generic assistant asked to "modernize this SuiteScript" will produce the loop above, because nothing in the JavaScript itself says the indexing base changed. An assistant holding the mapping table checks the loop because the table tells it to. We wrote separately about what the skill means for teams already on SuiteScript 2.0, where the deprecation question and the migration queue matter more than the call-level differences. Code coming from 1.0 is the harder case, because the runtime conventions changed and not only the names.
The booleans that flipped meaning
Sublist indexing at least has a chance of being caught by a developer who remembers the rule. Inverted flags do not, because the converted line reads as a faithful copy of the original.
In SuiteScript 1.0, a client script suppressing dependent field logic wrote it like this:
nlapiSetFieldValue('custbody_discount_code', code, false, false);
The third argument is firefieldchanged, and false means do not fire the change event. The SuiteScript 2.1 equivalent takes ignoreFieldChange, which is the same switch with the opposite polarity: it defaults to false, and setting it to true is what suppresses the event.[5]
currentRecord.setValue({
fieldId: 'custbody_discount_code',
value: code,
ignoreFieldChange: true
});
Carry the original false across and the script now fires the field change it was written to avoid, which reruns sourcing, dependent scripts, and whatever recalculation sits behind them. Nothing throws. The record simply ends up in a different state than it did last month.
The skill treats this as a named category rather than a footnote, and the same pattern shows up in the UI properties: setVisible(true) becomes isHidden = false, so a mechanical copy of the boolean hides the field it was supposed to show.[4]
Its detection logic is built around the same idea. An explicit @NApiVersion 1.0 tag is treated as definitive. Global nlapi* calls, nlobj* constructors, a missing AMD wrapper, and bare-function entry points are treated as strong signals in its absence, which is how the skill classifies the undated files that accumulate in a long-lived account.[4]
Thirteen APIs where the mapping table stops
The skill is explicit that 13 APIs have no direct 2.1 equivalent, and Oracle's own transition guidance says the same thing in prose: there is not always a direct mapping between functions and objects in SuiteScript 1.0 and the modules and methods available in SuiteScript 2.x.[6]
Recovery points are the clearest example. A scheduled script built around them does not convert into a 2.1 scheduled script; it converts into a decision about whether the work belongs in a Map/Reduce script instead. That is an architecture change with its own governance profile, its own failure behavior, and its own test plan, and no mapping table can make it a mechanical edit.
Everything past that point is account knowledge the skill does not have. Whether a search result feeds a display column or a financial posting, whether an empty value means zero or not yet calculated, whether a User Event runs before or after another deployment touching the same record: none of it is visible in the source file. A conversion that clears validation and clears Sandbox can still change the timing of an approval. This is also where governance behavior diverges, since the 2.1 replacement for a 1.0 call does not necessarily cost the same units.
Running it read-only first
Installation goes through the open Agent Skills CLI, and a project can list what is in the repository before installing anything.[2]
npm i skills -g
npx skills add oracle/netsuite-suitecloud-sdk --list
npx skills add oracle/netsuite-suitecloud-sdk \
--skill netsuite-suitescript-upgrade -a codex
The first prompt should not write anything:
Use $netsuite-suitescript-upgrade to analyze this SuiteScript 1.0 codebase.
Do not modify files. Inventory script types, entry points,
nlapi*/nlobj* usage, unmapped APIs, governance risks,
and produce a migration plan with test cases.
What comes back is an inventory, and it is worth reading as one. The detected script type, the unmapped APIs, and the high-severity behavioral flags are the parts that decide how long the project takes. The conversion mode can wait until one representative script has gone through analysis, review, validation, and a Sandbox comparison against the original, and until moving it forward has its own plan, which is a separate exercise with separate failure modes.
There is one more reason the bundled reference data is worth having. Oracle no longer publishes the SuiteScript 1.0 documentation as Help Center topics. It exists as two PDFs, SuiteScript10.pdf and SuiteScript10API.pdf, and reading either one requires signing in to a NetSuite account first.[7] The half of the mapping an agent needs to reason about legacy code is now behind a login, which is exactly the half Oracle packaged into the skill.
Sources
[1] Oracle NetSuite, CurrentRecord.getSublistValue(options)
[2] Oracle NetSuite SuiteCloud SDK, Agent Skills repository
[3] Oracle NetSuite, SuiteCloud Agent Skills Introduction
[4] Oracle NetSuite, netsuite-suitescript-upgrade SKILL.md
[5] Oracle NetSuite, Record.setValue(options)
[6] Oracle NetSuite, Transitioning from SuiteScript 1.0 to SuiteScript 2.x
[7] Oracle NetSuite, SuiteScript 1.0 Documentation