Josh,
Perhaps it may be good for you to publish here all the code that you are using so that we can see what is going on.
Just from the code snippet below, the code would not run in an installed trigger because you are using:
Browser.inputBox("Mail Merge",
This would stop the trigger from running just because the code is there.
Paste in your full code and we will be able to provide you guidance.
--
You received this message because you are subscribed to the Google Groups "Google Apps Script Community" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-apps-script-c...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/e5713d08-a617-434e-96c6-5832afec8e18n%40googlegroups.com.
Josh,
That error is from your function getGmailTemplateFromDrafts. If it fails for any reason, it gives that error. Not sure what the failure is because it depends on whether you have drafts defined in your gmail account and with the required subject_line that you seek.
If you want, remove the try/catch from that function and this way when it fails, it will give you the actual line of code that failed so you can better determine how to correct.
Here is the function without the try/catch sequence. Try it out and let us know. This really depends on your environment (what drafts you have defined or not) so we can not debug it without the specific errors.
function getGmailTemplateFromDrafts_(subject_line){
// get drafts
const drafts = GmailApp.getDrafts();
// filter the drafts that match subject line
const draft = drafts.filter(subjectFilter_(subject_line))[0];
// get the message object
const msg = draft.getMessage();
// Handles inline images and attachments so they can be included in the merge
// Based on https://stackoverflow.com/a/65813881/1027723
// Gets all attachments and inline image attachments
const allInlineImages = draft.getMessage().getAttachments({includeInlineImages: true,includeAttachments:false});
const attachments = draft.getMessage().getAttachments({includeInlineImages: false});
const htmlBody = msg.getBody();
// Creates an inline image object with the image name as key
// (can't rely on image index as array based on insert order)
const img_obj = allInlineImages.reduce((obj, i) => (obj[i.getName()] = i, obj) ,{});
//Regexp searches for all img string positions with cid
const imgexp = RegExp('<img.*?src=cid:(.*?).*?alt="(.*?)"[^\>]+>', 'g');
const matches = [...htmlBody.matchAll(imgexp)];
//Initiates the allInlineImages object
const inlineImagesObj = {};
// built an inlineImagesObj from inline image matches
matches.forEach(match => inlineImagesObj[match[1]] = img_obj[match[2]]);
return {message: {subject: subject_line, text: msg.getPlainBody(), html:htmlBody},
attachments: attachments, inlineImages: inlineImagesObj };
/**
* Filter draft objects with the matching subject linemessage by matching the subject line.
* @param {string} subject_line to search for draft message
* @return {object} GmailDraft object
**/
function subjectFilter_(subject_line){
return function(element) {
if (element.getMessage().getSubject() === subject_line) {
return element;
}
}
}
}
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/9f14192e-41b2-4d67-bce4-28d4609bfd41n%40googlegroups.com.
So the error is what I thought it was, you have no drafts defined that match your filter criteria.
const drafts = GmailApp.getDrafts();
// filter the drafts that match subject line
const draft = drafts.filter(subjectFilter_(subject_line))[0];
// get the message object
const msg = draft.getMessage();
Define some mail drafts in your gmail and the code should work. The subject line it is looking for in the filter is this:
"Historic Englewood - It's Time to Renew Your Membership!"
So your drafts need to have that subject line in it.
George
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/a33bf4b9-f95b-4a06-96c5-0039a16fa514n%40googlegroups.com.

To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/f170e6b0-e793-488f-bbae-49abf7b15e20n%40googlegroups.com.
Josh,
Your code was failing because of the array method used.
This code that you had was not succeeding as filter was not working as expected.
const draft = drafts.filter(subjectFilter_(subject_line))[0];
// get the message object
const msg = draft.getMessage();
If you want to bypass that, try this code to replace it.:
var msg = null;
const drafts = GmailApp.getDrafts();
// filter the drafts that match subject line
for (var i = 0;i<drafts.length;i++)
{
msg = drafts[i].getMessage();
if(msg.getSubject() == subject_line)
{
drafts = drafts[i];
break;
}
}
msg = draft.getMessage();
Code does the same thing but used a for loop to look for correct draft rather than a filter method.
From: google-apps-sc...@googlegroups.com <google-apps-sc...@googlegroups.com> On Behalf Of Josh Goldstein
Sent: March 29, 2022 9:25 PM
To: Google Apps Script Community <google-apps-sc...@googlegroups.com>
Subject: Re: [Apps-Script] Re: MailMerge - getGmailTemplateFromDrafts fails when called on trigger
Hi Martin,
Thanks for your detailed instructions.
I ran your test, and indeed, there are no errors.
Any idea what could be different about my code that is causing the draft to not be found?
Josh
On Thursday, March 24, 2022 at 4:16:59 AM UTC-6 m.ha...@gmail.com wrote:
Hi Josh,
Personally I've not encountered issues with array methods used in triggers but happy to be proved wrong. Perhaps it might be worth taking a step back and rather than modifying the getGmailTemplateFromDrafts function to test with a setup I've tested and not encountered any issues with? The way I've scheduled mail merge emails is:
- Make a copy of the mail merge solution at https://developers.google.com/apps-script/samples/automations/mail-merge
- Update the sheet with a test recipient email address
- In your copied version of the mail merge open the Script Editor (Extensions > Apps Script) and add the following lines of code (I inserted this at line 30 in my test):
function sendScheduledEmails() {
const sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1'); // <-- Update with sheet name to use data from
const subjectLine = 'Historic Englewood - It's Time to Renew Your Membership!'; // <-- Update with email draft subject line (to avoid errors copy/paste from the draft subject line)
sendEmails(subjectLine, sheet);
}
- In the Script Editor from the left hand menu select 'Triggers' and click 'Add Trigger' selecting the 'sendScheduledEmails' function and setting a specific date/time in the future (useful to set the 'failure notification settings' to notify me immediately')
- Click 'Save' which if running for the first time should prompt authorization for the script. Important: If running this from a @gmail.com account at the 'Google hasn’t verified this app' you need to click Advance and then click the 'Go to Gmail/Sheets mail merge (unsafe)'
- Click 'Save' on the trigger dialog
A general note is when using this solution is everything is case sensitive (subject lines, column headings, replacement tokens all need to match exactly).
The modified copy of the Mail Merge example I tested with is here https://docs.google.com/spreadsheets/d/17thg6_FA8WGvr5bJobpJgZDEgezW29dVetikAreeE04/copy
A copy of the Gmail text I copied/pasted into Gmail is here https://docs.google.com/document/d/15p7YVZ78VF-B5ywF1rV5AHBIzIS6XvIdX1NVs4EpWf4/edit?usp=sharing
Let us know how you get on
Best
Martin
On Thursday, 24 March 2022 at 02:56:40 UTC George wrote:
Hi Josh,
Looked a little further at this error for you and it seems to be caused by the use of the Array methods. (Such as Filter, Index Of, etcc) Most are not supported during triggers. The code would have to be redone not to use them for it to work properly.
On Tue., Mar. 22, 2022, 8:35 a.m. Josh Goldstein, <jdeango...@gmail.com> wrote:
But I do have a draft with that exact subject line. As I've said multiple times, this works as expected when run manually from the script editor, but only fails when run via trigger.
There is already, absolutely, a draft with that subject line in the Gmail account. If there wasn't a draft, it wouldn't work when I run the script manually.
The issue is that the draft seems to not be found when run via trigger and that's the part I'm trying to figure out.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/a9215931-f740-4f49-8f4e-cc6f4e5c3685n%40googlegroups.com.
Thanks for catching the typo.
I do not understand why the array methods do not work while under trigger execution, yet they run fine during manual invocation. I ran across this several times in the past, but definitely inconsistenly. It may be a quirk of the Google Run-time system.
If someone opens an issue on this it would be great. But I suspect it would be a low priority compared to other problems they may be facing.
If someone else has an explanation for this, I would love to hear it. Besides the Filter method, I have seen this with the IndexOf method as well.
Thanks,
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/962987f0-1d3a-4208-9b74-081f7d1caf85n%40googlegroups.com.
I went ahead and swapped out another Array Method.. Tested it and it worked for me. Let me know what you see.
George
Here is the resulting code:
/**
* Define the relevant sheet, columns, and other data required to run the automatic renewal email script
**/
const SPREADSHEET_ID = '18JKQMoMiqN6BgMeg24RdJUnTWHPu0teEXllb_b33fWQ';
const SHEET_NAME = "Testsheet"; /** the sheet from which all data is read - use "TestSheet" for debugging */
const NUMBER_OF_HEADER_ROWS = 2; /** specify how many rows from the top of the spreadsheet consist of header data (for column searching) */
const RENEWAL_DATE_COL = 'NEXT RENEWAL DATE'; /** the column name from which the renewal date is read and compared to the current date */
const RECIPIENT_COL = 'EMAIL'; /** the column name from which the email address will be read from */
const EMAIL_SENT_COL = 'AUTOMATIC RENEWAL EMAIL SENT'; /** the column name in which to record the successful email dispatch date */
const EMAIL_SUBJECT_LINE = "Historic Englewood - It's Time to Renew Your Membership!"; /** only use an email draft with this subject line */
const DAYS_BEFORE_RENEWAL = 30; /** the number of days before the renewal date that an email should be sent */
/**
* Creates the menu item "Mail Merge" for user to run scripts on drop-down.
* Note: This script is tied to a trigger, so running manually is not necessary except for debugging
**/
function onOpen() {
const ui = SpreadsheetApp.getUi();
ui.createMenu('Mail Merge')
.addItem('Send Emails', 'sendMembershipRenewalEmails')
var msg = null;
const drafts = GmailApp.getDrafts();
// filter the drafts that match subject line
for (var i = 0;i<drafts.length;i++)
{
msg = drafts[i].getMessage();
if(msg.getSubject() == subject_line) { break;}
}
// Handles inline images and attachments so they can be included in the merge
// Based on https://stackoverflow.com/a/65813881/1027723
// Gets all attachments and inline image attachments
const allInlineImages = msg.getAttachments({includeInlineImages: true,includeAttachments:false});
const attachments = msg.getAttachments({includeInlineImages: false});
const htmlBody = msg.getBody();
// Creates an inline image object with the image name as key
// (can't rely on image index as array based on insert order)
const img_obj = allInlineImages.reduce((obj, i) => (obj[i.getName()] = i, obj) ,{});
//Regexp searches for all img string positions with cid
const imgexp = RegExp('<img.*?src=cid:(.*?).*?alt="(.*?)"[^\>]+>', 'g');
const matches = [...htmlBody.matchAll(imgexp)];
//Initiates the allInlineImages object
const inlineImagesObj = {};
// built an inlineImagesObj from inline image matches
matches.forEach(match => inlineImagesObj[match[1]] = img_obj[match[2]]);
return {message: {subject: subject_line, text: msg.getPlainBody(), html:htmlBody},
attachments: attachments, inlineImages: inlineImagesObj };
} catch(e) {
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/0ed70951-a354-4011-99fe-489df7e072bdn%40googlegroups.com.
Have you tried it and did it work for you?
I had initially removed the use of the function subjectFilter_ and then removed the complete use of the .filter method with a replacement to do a For loop and check draft title name instead and that seemed to have done the trick.
In some of my other triggers that I use, the usage of indexOf gave me trouble and I had to replace it with a specific loop to do the check instead. But it does not seem to cause problems in this application. Go figure the inconsistency..
Let me know what you got.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/f954952d-6667-4725-8f29-defe63f48d1an%40googlegroups.com.
You should be able to get all the changes just by swapping out function getGmailTemplateFromDrafts_ of what you have versus the code I put in place.
Let me know if you have any other difficulties. Feel free to send me a private email.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-apps-script-community/48b2f43d-a914-4370-a2fd-dd93474bc720n%40googlegroups.com.