// Apps Script

Get the active user's email in Apps Script (and why it's sometimes blank).

Session.getActiveUser().getEmail() returns an empty string for cross-domain viewers and form respondents — learn exactly when it works, when it silently fails, and how to pull the real email from event objects instead.

I called Session.getActiveUser().getEmail() in my onFormSubmit trigger and got an empty string, and I can't figure out why.

The script

copy · paste · trigger
getEmail.gs
Apps Script
// Get respondent email from a form-submit event
// Session.getActiveUser() is unreliable here — use the event object instead

function onFormSubmit(e) {
  var response = e.response;
  var email = response.getRespondentEmail();

  // Falls back to trigger owner if form doesn't collect email
  if (!email) {
    email = Session.getEffectiveUser().getEmail();
  }

  Logger.log('Email: ' + email);

  var sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();
  sheet.appendRow([new Date(), email]);
}

Need a variant? Gnaw writes a custom version from one sentence — fields, triggers, edge cases handled.

Walkthrough

Why getActiveUser() goes quiet

Session.getActiveUser().getEmail() returns the email of the person currently running the script — but only under a specific condition: the user must be in the same Google Workspace domain as the script owner, and the script must be running in a context where Apps Script can verify their identity. Outside that narrow window, Google returns an empty string rather than throwing an error.

The frustrating part is that it silently fails. You call getEmail(), get back "", and there's no exception, no log warning, nothing. The first time I hit this I spent an hour checking OAuth scopes before realizing the form respondent was on a personal Gmail account, which is cross-domain by definition.

Three situations reliably produce a blank: a form respondent on a different domain (or any personal Gmail), an installable trigger running as the trigger owner rather than the viewer, and any script deployed as a web app with "Execute as me" where the caller's identity isn't passed through. The common thread is that Apps Script's identity model only surfaces the active user when it can guarantee the claim without leaking PII across org boundaries.

Pulling the email from the event object instead

For Google Forms, the right source is e.response.getRespondentEmail(). This works when "Collect email addresses" is enabled in the form settings (Forms > Settings > Responses). The value comes from the form submission itself, not from a session identity check, so it works regardless of domain.

Note that getRespondentEmail() returns an empty string when the form doesn't have email collection turned on — it won't throw. The snippet above handles this by falling back to Session.getEffectiveUser().getEmail(), which returns the trigger owner's email. That's not the respondent's email, but it's at least a real value you can use for routing logic or audit trails.

For onEdit triggers in Sheets, the event object doesn't carry respondent email, but e.user.email is available when the editor is in the same Workspace domain. Outside that domain it's blank for the same privacy reason. If you need cross-domain editor identity in Sheets, the realistic answer is a web app endpoint that accepts a POST from a client-side script running as the viewer — which gets a different set of permissions.

getActiveUser vs getEffectiveUser — the distinction that matters

Apps Script exposes two methods that sound nearly identical. Session.getActiveUser() attempts to return the person interacting with the UI right now. Session.getEffectiveUser() returns whoever the script is authorized to run as — almost always the trigger owner or the person who set up the OAuth grant.

In a time-driven or form-submit installable trigger, getActiveUser() is always blank because there is no active UI session; the trigger fires in the background. getEffectiveUser() will reliably return the trigger owner's email in that context. I keep a small utility at the top of trigger files that logs getEffectiveUser() on first run, just to confirm whose credentials are driving the automation.

The scope you need for either call is https://www.googleapis.com/auth/userinfo.email. If that scope is missing from your appsscript.json oauthScopes array and you're relying on auto-detection, Apps Script may not request it at all, which produces another source of empty strings that's easy to confuse with the domain-privacy case.

Want a custom version?

Describe your sheet and the rule you want. Gnaw writes the Apps Script — fields, triggers, edge cases — in one shot.

FAQ

4 questions
Session.getActiveUser().getEmail() works when I run the script manually but returns blank when the trigger fires automatically — why?
Installable triggers run in a background context with no active user session. getActiveUser() requires an interactive UI session to identify a user. Use Session.getEffectiveUser().getEmail() instead, which returns the trigger owner's email regardless of execution context.
My form collects email addresses but e.response.getRespondentEmail() still returns empty — what's wrong?
Check that "Collect email addresses" is enabled under the form's Settings > Responses tab, not just that you see an Email column in the response sheet. If the form was created before that setting was turned on, existing submissions may lack the field. Also confirm you're accessing e.response, not e.values — e.values is a flat array of answer strings, not a FormResponse object.
I need the email of whoever is editing a shared Sheet, including external collaborators. Is there any way to get it?
Not through a simple trigger. e.user.email in an onEdit trigger is blank for users outside your Workspace domain. The only reliable cross-domain approach is a custom web app that runs as the accessing user ("Execute as: User accessing the web app") and reads Session.getActiveUser().getEmail() server-side after the user authenticates — but that requires the editor to explicitly open the web app, not just edit the sheet.
What OAuth scope do I need to add so getEmail() stops returning blank?
Add https://www.googleapis.com/auth/userinfo.email to the oauthScopes array in your appsscript.json. Without it, Apps Script may never request the permission at all, and the method returns an empty string silently. After adding the scope, you'll need to re-authorize the script — existing authorizations won't pick up the new scope automatically.
// one good script a week

Get a working Apps Script snippet in your inbox, weekly.