Get UI Safe

SpreadsheetApp.getUi() only exists when a user has the spreadsheet open. Time-driven and many installable triggers are headless — calling getUi() throws and aborts the run. getUiSafe returns the UI or null so onOpen menus and shared helpers don't blow up overnight jobs.

What you'll need

  • A container-bound Sheets script (UI is spreadsheet-scoped)
  • Code shared between onOpen / menus and time-driven / installable triggers
  • A habit of branching on ui instead of assuming a dialog works

How to use this snippet

/** * Return SpreadsheetApp.getUi() when a UI exists; otherwise null. * Safe for time-driven / installable headless executions. * @return {GoogleAppsScript.Base.Ui|null} */ function getUiSafe() { try { return SpreadsheetApp.getUi(); } catch (e) { return null; } }

Example

function onOpen() { var ui = getUiSafe(); if (!ui) return; ui.createMenu('Ops') .addItem('Run nightly sync now', 'nightlySync') .addToUi(); } function nightlySync() { // works from menu OR time-driven trigger var result = syncLeads_(); var ui = getUiSafe(); if (ui) { ui.alert('Sync done', 'Rows: ' + result.count, ui.ButtonSet.OK); } else { console.log('nightlySync ok', result); } }

Tips:

  • Never put getUi() at the top of a shared library function without a guard.
  • Simple onOpen is UI-context; installable onOpen still has UI when a user opens the file — time-driven does not.
  • Use ui.alert / menus only after the null check; log or email for headless paths.
  • Pair with emailErrorAlert when headless failures need a human.

Tip: NitroGAS Co-Pilot is handy for splitting "menu path" vs "trigger path" in one function without duplicating the real work.

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