エラーの内容
Google Sheetsのスクリプトにコードを組み込んで、メール送信、外部API呼び出し、簡単なアラート表示などを行おうとしたところ、トリガーが突然動かなくなることがあります。Apps Scriptの実行ログを確認すると、次のようなエラーが表示されます:
Exception: The script does not have permission to perform that action.
厄介なのは、エディターから同じ関数を手動で実行すると正常に動作する点です。ところがトリガーが起動した瞬間にエラーが発生します。原因と解決策を説明します。
なぜこのエラーが起きるのか
Apps Scriptにはトリガーが2種類あり、内部の動作がまったく異なります:
- シンプルトリガー —
onEdit(e)、onOpen(e)、onChange(e)のような予約済み関数名です。自動的に起動しますが、ユーザー認証情報が付与されていないサンドボックス環境で実行されます。OAuth認証が必要な操作は一切実行できません。 - インストール可能なトリガー — トリガーUIまたは
ScriptAppのコードで明示的に作成するトリガーです。作成者の認証情報で実行され、設定時に承認された完全な権限セットを使用できます。
この制限は絶対的です。appsscript.jsonに必要なOAuthスコープをすべて記載していても、シンプルトリガーはそれらにアクセスできません。サンドボックスはトークンを完全に無視します。
シンプルトリガー内で確実に失敗する操作:
MailApp.sendEmail()またはGmailApp.sendEmail()UrlFetchApp.fetch()— 外部へのHTTPリクエスト全般SpreadsheetApp.getUi().alert()やUIダイアログ全般DriveApp.createFile()やDriveの操作全般- カレンダー、連絡先、Admin SDK の呼び出し
解決策:インストール可能なトリガーへの切り替え
ステップ1 — 関数名を変更する
onEditという名前の関数は、登録方法に関わらず Apps Script によってシンプルトリガーとして扱われます。予約リストにない名前に変更してください:
// 変更前 — シンプルトリガー、権限が制限される
function onEdit(e) {
if (e.range.getColumn() === 3) {
MailApp.sendEmail('boss@company.com', 'Row updated', 'Someone edited column C');
}
}
// 変更後 — シンプルトリガーとして扱われないよう名前を変更
function onEditHandler(e) {
if (e.range.getColumn() === 3) {
MailApp.sendEmail('boss@company.com', 'Row updated', 'Someone edited column C');
}
}
ステップ2 — インストール可能なトリガーを作成する
方法A — UIから設定(一度きりの設定に最適):
- Apps Scriptエディターでスクリプトを開きます。
- 左サイドバーの時計アイコン(トリガー)をクリックします。
- 右下の**+トリガーを追加**をクリックします。
- 関数を
onEditHandler、イベントのソースをスプレッドシートから、イベントの種類を編集時に設定します。 - 保存をクリックすると、スクリプトの承認を求めるダイアログが表示されます。
方法B — コードで設定(一度だけ実行してから削除):
function createEditTrigger() {
const ss = SpreadsheetApp.getActiveSpreadsheet();
// 重複を避けるため、既存のトリガーを削除する
ScriptApp.getProjectTriggers().forEach(t => {
if (t.getHandlerFunction() === 'onEditHandler') {
ScriptApp.deleteTrigger(t);
}
});
ScriptApp.newTrigger('onEditHandler')
.forSpreadsheet(ss)
.onEdit()
.create();
console.log('Installable trigger created successfully.');
}
エディターからcreateEditTrigger()を実行し、承認のポップアップを許可してください。以降、トリガーは編集のたびに完全なOAuth認証情報で実行されるため、権限エラーは発生しません。
ステップ3 — OAuthスコープを確認する
承認後もエラーが続く場合は、appsscript.jsonマニフェストを確認してください。プロジェクトの設定 → appsscript.jsonマニフェストファイルを表示するから有効化できます:
{
"timeZone": "America/New_York",
"dependencies": {},
"exceptionLogging": "STACKDRIVER",
"runtimeVersion": "V8",
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/gmail.send",
"https://www.googleapis.com/auth/script.external_request"
]
}
マニフェストを更新したら、エディターから任意の関数を一度実行して、新しいスコープリストで再承認を行ってください。
修正の確認
- トリガーページ(時計アイコン)を開きます。
onEditHandlerが種類編集時、ソーススプレッドシート、所有者列にあなたのメールアドレスで表示されているか確認します。 - スプレッドシートのセルを編集してトリガーを起動させます。
- 左サイドバーのリストアイコンから実行数を確認します。正常に完了した場合はステータスが完了と表示されます。まだ権限エラーが出る場合はステップ3に戻って再承認してください。
- 実際の処理が行われたか確認します — 受信トレイへのメール、ログへのAPIレスポンスなど、スクリプトが行うはずの処理が実行されているか確かめます。
トリガーが自分のアカウントで実行されているか簡単に確認したい場合は、次の行を一時的に追加してください:
function onEditHandler(e) {
console.log('Effective user:', Session.getEffectiveUser().getEmail());
// ... 以降のコード
}
実行ログにメールアドレスが表示されればインストール可能なトリガーが正しく設定されています。何も表示されないかエラーが出る場合は、どこかでシンプルトリガーとして起動しています。
注意:トリガーの重複
createEditTrigger()を2回実行すると、同じハンドラーを指すトリガーが2つ登録されます。編集のたびに2回起動し、メールが2通送信され、APIが2回呼び出されます。セットアップ関数を再度実行する前にトリガーページを確認するか、上記のコードに含まれている重複排除ロジックを活用してください。現在登録されているトリガーを確認するには:
function listTriggers() {
ScriptApp.getProjectTriggers().forEach(t => {
console.log(
t.getHandlerFunction(),
t.getEventType(),
t.getTriggerSourceId()
);
});
}
補足のヒント
- シンプルトリガーはシンプルに使う。
onEditはセルの書式設定や「最終更新日時」のスタンプなど、認証不要の軽い処理にだけ使いましょう。外部サービスに触れる処理はインストール可能なトリガーで行うべきです。 - 共有スクリプトではトリガーの所有者が重要。 インストール可能なトリガーは作成者の認証情報で実行されます。チームで共有するシートでは、自分のアカウントで自動化を実行したい各メンバーがそれぞれトリガーを作成するか、サービスアカウントを1つ決めて共有トリガーを設定してください。
- スコープは必要最小限に。 実際に使用するOAuthスコープのみをリクエストしてください。
gmail.sendで十分なのにhttps://mail.google.com/を指定すると、承認審査の妨げになり、アクセスを許可するユーザーにも混乱を与えます。 - 時間ベースのトリガーはこの問題の影響を受けない。
ScriptApp.newTrigger().timeBased()でスケジュールされたジョブは、本質的に常にインストール可能なトリガーです。この制限はonEditやonOpenのような予約済み関数名のイベントベーストリガーにのみ適用されます。

