DEV Community

bulldo.gs
bulldo.gs

Posted on • Originally published at bulldo.gs

Switch an old script from Rhino to the V8 runtime

Originally written for bulldo.gs — republished here with the canonical link pointing home.

I want to enable the V8 runtime on an existing Apps Script project so I can use modern JavaScript, but I am worried about breaking things that are already working.

// appsscript.json — set runtimeVersion to enable V8
// Rollback: change V8 back to DEPRECATED_ES5
{
  "timeZone": "America/New_York",
  "dependencies": {},
  "exceptionLogging": "STACKDRIVER",
  "runtimeVersion": "V8"
}

// Code.gs — safe V8-compatible replacement for a common Rhino pattern
// Rhino allowed: for each (var item in collection) {}
// V8 requires standard for...of instead
function listSheetNames() {
  var ss = SpreadsheetApp.getActiveSpreadsheet();
  var names = [];
  var sheets = ss.getSheets();
  for (var i = 0; i < sheets.length; i++) {
    names.push(sheets[i].getName());
  }
  Logger.log(names.join(', '));
}
Enter fullscreen mode Exit fullscreen mode

The one-line change and why it is a project-wide bomb

Open the Apps Script editor, click Project Settings (the gear icon), and check "Show appsscript.json manifest file in editor." Then open that file and change "runtimeVersion": "DEPRECATED_ES5" to "runtimeVersion": "V8". Save. That is the entire migration from a settings standpoint.

What catches people off guard is the failure mode. V8 parses every .gs file in the project as a unit before running anything. One Rhino-only statement — a for each loop, a __iterator__ method, a Date.prototype.getYear call in an otherwise untouched utility file — causes a syntax or runtime error that prevents the whole script from initializing. Not just the file that contains the bad line. Every function, every trigger, the entire project goes dark.

The first time I hit this it took me twenty minutes to figure out why a completely unrelated trigger had stopped firing. The error message pointed at the Rhino syntax in a helper file I had not touched in two years. V8 does not isolate the damage; it fails at parse time, before any execution happens.

Finding Rhino-only syntax before you flip the switch

The three patterns that break most often under V8 are: for each (var x in obj) (a Mozilla-specific extension Rhino supported, not standard ES), XML literals like var x = <tag/>, and __iterator__ or __defineGetter__ on prototypes. Do a text search across all .gs files for those strings before you change runtimeVersion.

The Apps Script editor has no project-wide search, so either use the browser's find-in-page on each file, or copy all your .gs files into a local editor with multi-file search. Any hit needs to be rewritten: for each becomes a standard for...of or indexed for loop; XML literals need to be replaced with XmlService calls or plain strings; the prototype methods have no direct replacement and usually need a logic rewrite.

After fixing the files, change runtimeVersion to V8, save, and immediately run one function manually from the editor toolbar. If the Execution Log shows the function completing, your triggers are also alive. If you see a red syntax error, V8 caught something the search missed — read the error, fix the line, repeat.

Instant rollback while you keep fixing files

If production triggers are firing and you need to un-break them immediately, set "runtimeVersion": "DEPRECATED_ES5" in appsscript.json and save. Rhino resumes within seconds. You do not need to revert any .gs file changes you already made — DEPRECATED_ES5 tolerates both old and new syntax.

This lets you migrate file by file. Fix one .gs file, flip to V8, test, flip back to DEPRECATED_ES5, fix the next file. Tedious for large projects, but it keeps triggers running throughout. Once every file passes a V8 test run, leave runtimeVersion set to V8 permanently.

One practical note: the DEPRECATED_ES5 value is exactly that — deprecated. Google has not announced a hard removal date as of mid-2026, but they have signaled V8 is the intended path. Old scripts will keep running on Rhino for now, but new V8-only APIs (like globalThis or native Promise support in triggers) are not back-ported, so staying on Rhino indefinitely means foregoing those.

FAQ

After I set runtimeVersion to V8, all my triggers stopped running with no error in the dashboard. What happened?

A parse-time error in any .gs file silently kills initialization. Open the editor, run any function manually, and look at the Execution Log — it will show the actual syntax error and the file name. Fix that line, then triggers resume automatically without redeployment.

Can I use arrow functions and const/let after switching to V8?

Yes. V8 supports ES2019 and most ES2020 syntax: arrow functions, const/let, destructuring, spread, async/await, optional chaining, and nullish coalescing. The one thing that does not work the same way is top-level await — Apps Script still requires async functions to be called from a synchronous trigger wrapper.

My script uses XmlService and it works fine on Rhino. Will it break on V8?

XmlService itself is a Google service and is runtime-agnostic — it works on both. What breaks under V8 is E4X, the old Rhino XML literal syntax (angle-bracket XML directly in JavaScript code). If your script uses XmlService.parse() and XmlService.getContentType() style calls rather than inline XML literals, it will migrate without changes.

Is there a way to see which runtime a deployed script is currently using without opening the manifest?

Not from outside the editor. You have to open the project, enable manifest display in Project Settings, and read appsscript.json. The Apps Script API (the REST API for managing scripts) does expose the runtimeVersion field in the project Content resource if you need to audit it programmatically across many projects.


Want the plain-English version? Describe the automation at bulldo.gs and get working Apps Script back — free, no login.

Top comments (0)