Install Menu Safely

Someone renames exportReport to exportMonthlyReport, the menu still points at the old name, and every click says “Script function not found”. Or onOpen throws and the menu doesn't appear at all. installMenuSafely(caption, items) builds the menu inside try/catch, skips (and logs) any item whose function isn't defined, and never throws out of onOpen. Items are [label, fnName], or null for a separator.

When to use

  • Every onOpen that builds a custom menu
  • Shared spreadsheets where several people edit the script
  • Menus that grow over time and occasionally lose a function

How to use this snippet

/** * Build a custom menu without letting a bad item break onOpen. * * @param {string} caption menu title * @param {Array<Array<string>|null>} items [label, functionName] or null for a separator * @return {{added: number, skipped: string[]}} */ function installMenuSafely(caption, items) { var result = { added: 0, skipped: [] }; try { var menu = SpreadsheetApp.getUi().createMenu(caption); (items || []).forEach(function (item) { if (item === null) { menu.addSeparator(); return; } var label = item[0], fnName = item[1]; if (typeof globalThis[fnName] !== 'function') { result.skipped.push(fnName); console.warn('installMenuSafely: skipped "' + label + '", ' + fnName + ' is not defined'); return; } menu.addItem(label, fnName); result.added++; }); menu.addToUi(); } catch (err) { // No UI (trigger or editor run) or a menu API error: log it, don't break onOpen console.warn('installMenuSafely: menu not installed: ' + (err && err.message ? err.message : err)); } return result; }

Example

function onOpen() { installMenuSafely('Reports', [ ['Refresh data', 'refreshData'], ['Export monthly report', 'exportMonthlyReport'], null, ['Settings…', 'showSettingsSidebar'] ]); }

Tips:

  • globalThis needs the V8 runtime (the default for new projects). On the old Rhino runtime, switch to V8 in Project Settings.
  • Keep onOpen this small. Anything slow or that needs authorization belongs in the menu functions, not in onOpen.
  • Check the Executions log after a rename: a skipped item shows up there as a warning.
  • Library functions aren't on globalThis. Wrap them in a local function and put that in the menu.

Tip: NitroGAS Co-Pilot can convert an existing onOpen menu to installMenuSafely. Paste your onOpen.

Happy Coding!

NitroGAS Chrome Extension

Want this snippet handy inside the Apps Script editor? NitroGAS gives you free themes and snippets — plus optional Co-Pilot when you want a boost (1-week, 1-month, or yearly passes — no subscription). Happy Coding!

Get the Extension