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. チェックリスト
- 貼る前に、両方のファイルの
const/var/functionの名前を並べて重複がないか見る onOpen・doGet・onEditなどの特別な関数は1つにまとめる- 保存後、エディタで何か1つ関数を実行して構文エラーが出ないか確認
- トリガー画面(時計アイコン)で、同じ関数に2つ付いていないか確認
- どうしても分からなければ、別のスプレッドシートに分ける
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事務自動化の基本