ワザ箱 暮らしと仕事の悩みを、手順で解決するサイト

GASの時間主導トリガーが重なって同じメールが二重に送られる

公開 2026年11月7日2026年11月時点の情報
先に結論

送信処理を LockService.getScriptLock() のロックで囲み、取れなかった回は何もせず終えます。
送る直前にその行へ「送信中」と書き、送り終えたら日時にします。

  1. 「トリガー」で同じ関数が2つ登録されていないか見る
    • 2つ以上ある1つを残して削除する
    • 1つだけ前の回と次の回が重なっている可能性
  2. 送信の関数をロック付きのコードに置き換える
  3. 「送信中」のまま残った行は自動では送り直さない
  4. 1行に1通だけ届けば解決

※2026年11月時点の公式ドキュメントに基づきます。コードは実機未実行で、模擬環境で確認済みです。

上空の2機の紙飛行機と、南京錠が載った柱の上の郵便受けの絵

エラーも出ずに二重になる理由

前の回が「未通知」と読む次の回も「未通知」と読む

送信済みを書く前に次の回が読むと、両方が送ってしまう(この記事の推測)

同時実行数の上限は1ユーザー30です。前の回が長引くと次の回と並ぶ、というのはこの記事の推測です。
出典: Google サービスの割り当て

1トリガーの重複登録を確かめる

  1. 通知の一覧のシートで「拡張機能」 → 「Apps Script」
  2. 左の「トリガー」で、同じ関数名が2行以上ないか見る
  3. 関数名と間隔(5分おき等)を控え、1行だけ残して縦3点の「その他」 → 「トリガーを削除」

他人のトリガーは自分の画面に出ないため、共同編集者にも見てもらいます(前任者のトリガー)。
出典: コンテナにバインドされたスクリプト / トラブルシューティング / インストール可能なトリガー

2ロック付きのコードに置き換える

1行目が見出し、A〜C列が宛先・件名・本文、D列が「通知済み」(空欄か日時。チェックボックス不可)の一覧が前提です。A列が宛先でない表には使えません。

  1. 古い関数のトリガーを、関数名と間隔を控えて削除
  2. シートの「拡張機能」から開いたエディタで(単独のスクリプトは対象外)、今の送信用の関数を消し、下のコードを定数2行ごと関数の外に貼って保存
  3. 上部のプルダウンで notifyNew を選び「実行」。確認画面が出たら案内に従って許可
  4. 「トリガーを追加」で notifyNew を控えた間隔の時間主導型で作り、「保存」

実行するとD列が空の行には本当に送られます。 送信済みを別の方法で管理していたなら全員に再送になるので、先に送り終えた行のD列を埋めます。試すなら自分宛ての1行だけ空けます。今の関数の添付やCCは引き継がれません。

const SHEET_NAME = 'シート1'; // ← 自分のシート名に
const DONE_COL = 4; // ← 「通知済み」の列番号(D列なら4)

function notifyNew() {
  const lock = LockService.getScriptLock();
  if (!lock.tryLock(1000)) {
    console.log('前の回が実行中のため終了');
    return;
  }
  try {
    const start = Date.now();
    const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(SHEET_NAME);
    const last = sheet.getLastRow();
    if (last < 2) return;
    const rows = sheet.getRange(2, 1, last - 1, DONE_COL).getValues();
    let row = 1;
    for (const r of rows) {
      row++;
      if (Date.now() - start > 4 * 60 * 1000) break;
      const to = r.shift(), subject = r.shift(), body = r.shift();
      if (!to || r.pop() !== '') continue;
      const cell = sheet.getRange(row, DONE_COL);
      cell.setValue('送信中');
      SpreadsheetApp.flush();
      MailApp.sendEmail(to, subject, body);
      cell.setValue(new Date());
    }
  } finally {
    SpreadsheetApp.flush();
    lock.releaseLock();
  }
}

待ち1秒と4分での区切りは、6分の実行時間の上限を避けるこの記事の提案です。
出典: 自動化のクイックスタート / インストール可能なトリガー / Class Range / Class Lock / Class LockService / Google サービスの割り当て

3「送信中」のまま残った行を片付ける

  1. エディタ左側の「実行」を開き、「フィルタを追加」 → 「ステータス」で「失敗」「タイムアウト」に絞る
  2. 失敗した回を開き、エラー文を見て原因を先に直す(この記事の提案)
  3. 止まった回の「送信中」の行の宛先に、届いたか聞く
  4. 「実行」に実行中の回が無いのを確かめ、届いていれば日時を手で入れ、なければセルを空にする(確認はこの記事の提案)

出典: ロギング

それでも二重に届く時のチェックリスト

出典: Class LockService / コンテナにバインドされたスクリプト

あわせて読みたい

メールの添付ファイルを毎回手でダウンロードして保存するのが面倒 スプレッドシートの行ごとにGoogleドキュメントの案内状を一括で作りたい GASで宛先ごとに名前や本文の一部を変えてメールを一括送信したい 問い合わせメールの仕分け・一次返信が手作業で追いつかない