実験トップ

GAS · エラー解決 · コード付き

GASで「Identifier has already been declared」。
複数のスクリプトを1つのプロジェクトで動かす。

ブログで見つけた Google Apps Script(GAS)を、すでに1本入っているプロジェクトに「+」でファイルを足して貼った。保存したら、どの関数を実行しても次のエラーが出て、前から動いていた1本目まで止まった。このページはその原因と直し方です。

SyntaxError: Identifier 'QUERY' has already been declared

1. 原因:GASのファイルは「別のファイル」ではない

Apps Script エディタではファイルが分かれて見えますが、実行時は全ファイルが1つのグローバル領域に並べて読み込まれます。つまり コード.gs と 添付保存.gs に同じ名前の const QUERY があると、同じ場所で2回宣言したのと同じ扱いになり、プロジェクト全体が構文エラーで動かなくなります。

ネットのコードは「単独で貼る」前提で書かれていることが多く、QUERY・SHEET_NAME・DRY_RUN・BATCH のような短い定数名はかなりの確率でぶつかります。

2. 何が衝突すると、何が起きるか

衝突するもの起きること
const / let の同名構文エラーで全ファイルが動かない(気づける)
var の同名エラーにならず、あとから読み込まれた値で黙って上書き。1本目が別の検索条件・別のシートで動き始める
function の同名(onOpen・main・getSheet_ など)エラーにならず、どちらか一方だけが使われる。メニューが片方しか出ない、補助関数の中身が入れ替わる
時間主導トリガー同じ関数に2つ登録すると2回ずつ動く(二重通知・二重保存)
LockService.getScriptLock()ロックはプロジェクトで1つ。同時刻に動いた別の処理は tryLock に失敗してその回はスキップ

var と関数名の衝突はエラーが出ないぶん、const より危険です。

3. 直し方A:いちばん簡単なのは「別のプロジェクトに分ける」

スクリプト同士でデータを受け渡さないなら、別のスプレッドシート(または別のスタンドアロンプロジェクト)に貼るのが確実です。衝突は起きません。ただし本数が増えると、どのシートに何が入っているか、どのトリガーが動いているかの管理が面倒になります。

4. 直し方B:同じプロジェクトに同居させる

(1) 定数に機能ごとの接頭辞を付ける

一番手軽です。ファイルの中だけで使われている名前を、機能名の接頭辞付きに置き換えます。文字列の中('label:' + DONE_LABEL の 'label:' 側など)まで置き換えないよう、エディタの置換は「単語単位」で。

// 添付保存.gs
const ATT_QUERY = 'has:attachment newer_than:3d';
const ATT_LIMIT_MS = 4.5 * 60 * 1000;

// 通知.gs
const NTF_QUERY = 'in:inbox newer_than:2d';

(2) 設定を1つのオブジェクトにまとめる

接頭辞より見通しがよく、あとから本数が増えても崩れません。

const ATTACH = {
  QUERY: 'has:attachment newer_than:3d',
  FOLDER_ID: 'ここにフォルダID',
  LIMIT_MS: 4.5 * 60 * 1000,
};

function saveAttachments() {
  const threads = GmailApp.search(ATTACH.QUERY, 0, 50);
  // ...
}

(3) 補助関数と onOpen を1つにする

sheet_() や esc_() のような補助関数名も衝突します。中身が違うなら名前を変え、同じなら1つだけ残します。onOpen は1プロジェクトに1つしか持てないので、両方のメニューを1つの onOpen の中で作ります。

function onOpen() {
  SpreadsheetApp.getUi().createMenu('自動化')
    .addItem('添付を今すぐ保存', 'saveAttachments')
    .addItem('新着を今すぐ通知', 'notifyNewMail')
    .addToUi();
}

メニューに指定する関数名は末尾に _ を付けないこと。_ で終わる関数は非公開扱いになり、メニューや「実行」から呼べません。

5. トリガーは「ONを2回押しても1つだけ」にする

同居させると、どの関数にトリガーが付いているか分からなくなりがちです。関数名で既存のトリガーを探し、あれば消す/なければ作るにしておくと、二重登録が起きません。他のスクリプトのトリガーは消さないよう、対象の関数名だけを見ます。

function toggleTrigger(fn, hours) {
  const existing = ScriptApp.getProjectTriggers()
    .filter(t => t.getHandlerFunction() === fn);
  if (existing.length) {                 // 動いていれば止める
    existing.forEach(t => ScriptApp.deleteTrigger(t));
    return false;
  }
  ScriptApp.newTrigger(fn).timeBased().everyHours(hours).create();
  return true;                           // 止まっていれば始める
}

6. ロックの取り合いは「取りこぼさない作り」で吸収する

LockService.getScriptLock() はプロジェクト全体で1つなので、同居させた処理どうしが同時刻に動くと、片方はロックを取れずにその回をスキップします。これ自体は二重実行を防ぐ正しい動きです。困るのはスキップした回のメールが永久に処理されない作りのときだけなので、「処理済みラベル」「処理済みIDの記録」「最後に処理した日時」のどれかで、次の回に続きから拾えるようにしておきます。

7. チェックリスト

Gmailの定番6本を、最初から同居できる形で使いたい方へ

このサイトの無料ガイド(添付保存・Slack/Chat通知・古いメール整理・送信元ラベル・シート書き出し・未返信リマインド)は1本ずつ単独で貼る前提です。セット版は、このページの方法で6本が1つのシートで同時に動くよう整理し、「今すぐ実行/定期実行ON・OFF(二重登録なし)/状態確認」メニューと日本語の導入手順を付けました。APIキー不要です。

Gmail自動化GAS 6本セット(APIキー不要/$9)を見る →

1本ずつ無料で試したい方へ:添付ファイルをドライブに自動保存 · 新着をSlack・Google Chatに通知 · 未返信を毎朝リマインド

Gumroad での販売です。デジタル納品のため、ダウンロード後の返金はお受けできません(不具合時は誠実に対応します)。

自分で設定するのが不安な方へ(ココナラ・日本円):シート1列のAI一括処理と、当日分の自動サマリーメールのGAS2本+日本語手順書を、ココナラで販売しています(3,000円・税抜〜)。初期設定サポート(+4,000円)や、あなたの実データで自動化フローを1つ一緒に作る導入伴走(+12,000円)は任意のオプションです(いずれも税抜)。対応できるかは購入前の見積り相談で確認できます。

ココナラで導入相談・購入する →

Gmailの古いメールを自動で削除・アーカイブ · Gmailを送信元ごとに自動でラベル分け · Gmailの受信メールをスプレッドシートに書き出す · GASからChatGPT APIを安全に呼ぶ · 無料本:GAS×ChatGPT事務自動化の基本

AI Revenue Experiment の用途解説ページです。導入実績や効果測定の結果ではありません。