Trigger failures are easy to miss unless someone watches the Executions page. Email alerts close that gap.
sendErrorEmailAlert_ formats a plain-text message with function name, timestamp, effective user, and stack.
runNightlySync demonstrates the try/catch around syncPartnerApi_ and rethrows after mailing.
Set ALERT_TO to a group inbox; authorize MailApp on first run.
Need this built? Hire a Google Apps Script developer →
Sheet / project setup
| Item | Value | Purpose |
|---|---|---|
| ALERT_TO | ops@example.com | Recipient |
| Entry | runNightlySync | Trigger target |
| MailApp | sendEmail | Delivery |
| Partner | /health | Sample failure source |
What this script does
On failure, ops receives an email then the error propagates.
Prerequisites
Mail send quota; ALERT_TO reachable; UrlFetch if using the sample sync.
- Authorize MailApp
- Use a group address
- Avoid emailing on every row in a loop
Walkthrough
Point health URL at a 500, run runNightlySync, confirm inbox + failed execution.
Edge cases
MailApp daily quotas can exhaust during outages — add CacheService cooldown per fnName.
- GmailApp alternative for from-alias needs
- HTML bodies via htmlBody optional
How to test
Throw new Error('test alert') inside the try block.
Hardening for production
Dedupe alerts for 30 minutes; include spreadsheet URL in the body.
Variations
Post to Chat webhook instead of or in addition to email.
Full code: sendErrorEmailAlert_()
Set ALERT_TO, attach a time-driven trigger to runNightlySync, and authorize MailApp once.
/**
* Email an alert when a guarded operation fails.
*/
const ALERT_TO = "ops@example.com";
function runNightlySync() {
try {
syncPartnerApi_();
} catch (err) {
sendErrorEmailAlert_("runNightlySync", err);
throw err;
}
}
function sendErrorEmailAlert_(fnName, err) {
const subject = "[Apps Script ALERT] " + fnName + " failed";
const body =
"Function: " + fnName + "\n" +
"When: " + new Date().toISOString() + "\n" +
"Effective user: " + Session.getEffectiveUser().getEmail() + "\n" +
"Message: " + (err && err.message ? err.message : err) + "\n\n" +
"Stack:\n" + (err && err.stack ? err.stack : "(none)") + "\n";
MailApp.sendEmail({
to: ALERT_TO,
subject: subject,
body: body,
});
}
function syncPartnerApi_() {
const resp = UrlFetchApp.fetch("https://api.example.com/health", { muteHttpExceptions: true });
if (resp.getResponseCode() !== 200) {
throw new Error("Partner health check HTTP " + resp.getResponseCode());
}
}- Line 4: Ops inbox or group that should wake up.
- Line 10: Builds subject/body and sends mail.
- Line 20: Shows which account the trigger runs as.
- Line 24: Sends the plain-text alert.
- Line 8: Sample dependency that can fail.
- Line 11: Preserves failed execution status.
Deploy this example
- 01
Open Apps Script
In the bound spreadsheet: Extensions → Apps Script. For standalone projects, create one at script.google.com and link your Sheet by ID.
- 02
Paste and save
Add a .gs file, paste the code below, rename constants at the top (sheet names, column letters, API property keys), then save.
- 03
Authorize once
Run the main function from the editor. Accept OAuth scopes when prompted — triggers cannot run until authorization succeeds once.
- 04
Add the trigger
Triggers → Add trigger → choose the handler function and event (time-driven, on edit, or on form submit). Delete test triggers before production.
Before you run: email alerts
- 1ALERT_TO is a monitored inbox
- 2MailApp authorized
- 3Trigger installed on runNightlySync
- 4Cooldown strategy if errors can flap
- 5No secrets in email bodies
- 6Test with a deliberate throw
Frequently asked questions
MailApp is simple for alerts. GmailApp is better when you need labels, aliases, or threads.
So the Executions list shows failure and retries/policies can react.
Use a cooldown key in CacheService or switch to Chat/PubSub for high volume.
Yes — set cc or pass a comma-separated to list.
Mail sends as the effective user unless you configure a send-as alias with GmailApp.
CacheService.getDocumentCache().get(fnName) — skip mail if present; put with 3600 TTL after send.
Add htmlBody alongside body for formatted stacks.