Apps ScriptトリガーでThe script does not have permission to perform that actionを修正する

intermediate📗 Google Sheets2026-05-31| Google Apps Script(V8ランタイム)、Google Sheets、Google Workspace — 全プラン

Error Message

Exception: The script does not have permission to perform that action.
#google-apps-script#トリガー#パーミッション#onEdit

エラーの内容

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()でスケジュールされたジョブは、本質的に常にインストール可能なトリガーです。この制限はonEditonOpenのような予約済み関数名のイベントベーストリガーにのみ適用されます。

Related Error Notes