Darren Hill Consulting Inc.

5 min read

Changing @NApiVersion is the easy part

What actually changes when a SuiteScript 2.0 script runs on the 2.1 engine

Oracle's timeline is now public. In 2026.2, SuiteScript 2.1 becomes the standard for new and existing scripts. In 2028.1, "SuiteScript 2.0/2.x scripts run as SuiteScript 2.1 by default." In 2028.2, "All new and existing scripts must use SuiteScript 2.1."

For a 2.0 script, the fix looks like one line: set the @NApiVersion tag to 2.1 and upload. That is Oracle's own instruction. But for a server script, that tag switches the JavaScript engine your code runs on. The engine is the migration. The annotation is just where you flip it.

Same API, different engine

"The functionality provided by the SuiteScript API is the same for SuiteScript 2.0 and SuiteScript 2.1," Oracle says. The language underneath is not: "SuiteScript 2.1 supports a newer ECMAScript version (ECMAScript 2023) than SuiteScript 2.0 (ECMAScript 5.1)."

Some of the differences fail loudly. for each...in and conditional catch blocks are now syntax errors. Calling toSource() throws. Reassigning a const, silently ignored in 2.0, throws a TypeError in 2.1. Those are the cheap ones: the script breaks in a sandbox, someone reads the log, it gets fixed.

The expensive ones don't throw.

Three that don't throw

Each of these reproduced in a NetSuite 2026.2 test account, under both versions.

The RESTlet that returns a string. Take Oracle's example:

// the following is included in a 2.x RESTlet
...
function post() {
    return JSON.stringify("flower");
}

In 2.0, "a JSON.stringify() call is added internally to whatever is passed in the post() method of a RESTlet," so the caller receives "\"flower\"": 12 characters, encoded twice. In 2.1 the caller receives "flower": eight. Return the bare string and it's eight in 2.0, six in 2.1.

Both versions return HTTP 200. Nothing fails inside NetSuite. What breaks is whatever parses the response — a middleware mapping, an integration platform, a partner's client — and the first person to notice probably won't be looking at NetSuite.

The error that loses its message. In my test account, JSON.stringify(new Error('msg')) returned {"message":"msg"} under 2.0 and {} under 2.1. That's standard ECMAScript: JSON.stringify serializes only enumerable properties, and the Error constructor creates message as a non-enumerable property when you pass it one. A script that stringifies an error into a log, a custom record or an outbound payload keeps running, and the message is missing from what it writes.

The number that loses its decimal. In Oracle's example, record.setValue('memo', 616.00) saves 616.0 in 2.0 and 616 in 2.1. A free-form text custom field did the same in my test. Whatever reads that field next now sees different text.

Oracle lists more, including trailing commas in JSON, toLocaleDateString(), parseInt and strict mode.

Oracle already shipped the test

You can try the 2.1 runtime before changing any code. Two account preferences — "Execute SuiteScript 2.0 Server Scripts as 2.1" and "Execute SuiteScript 2.x Server Scripts as 2.1" — run eligible server scripts on the 2.1 runtime without touching the annotation. In my test account, turning on the 2.0 preference was enough to change an unedited RESTlet's response from 12 characters to 8.

Read the fine print. "A script record with the Execute As field set to 2.0 continues to run as SuiteScript 2.0." And: "Client scripts are not affected by the server-script preference." A clean execution log can mean a script never ran on 2.1.

Oracle also says that "A script that NetSuite does not recognize as compatible with SuiteScript 2.1 continues to run as SuiteScript 2.0." That isn't what I saw. Three 2.0 Suitelets using for each...in, a conditional catch and a variable named extends worked with the preference off. With it on, each one failed with a SyntaxError. Turning the preference off brought them back. Try it in a sandbox, not production.

Oracle's AI agent skill for the conversion is blunt about its limits: "Validation is a static migration check, not a substitute for functional testing." A static check can confirm the annotation says 2.1. It can't tell you that a partner's integration expects 12 characters.

What I'd do

Start with the inventory: Customization > Scripting > Scripts, filtered on API Version.

Turn the server-script preference on in a sandbox, exercise each script against realistic test data, and read the execution logs. Then open each script record and check the Execute As Version field, which is where Oracle says to "confirm the runtime used for the script." Client scripts sit outside the preferences, so they need the annotation changed and their own test.

That surfaces the loud failures. For the quiet ones, ask one question per script: who consumes its output? For a RESTlet, it's the calling system, so test the response body, not the status code. For error handling, it's whoever reads the log. For a field write, it's whatever reads the field next.

Do the RESTlets first. When they break, the break is likely to show up in the systems that call them, not in NetSuite. And at 2028.1, 2.0 and 2.x scripts run as 2.1 by default.


Sources — Oracle NetSuite Help Center

Facing the 2.1 migration?

Get in touch

© 2026 Darren Hill Consulting Inc.