GAS『DriveApp cannot be used in this context』の原因と対処法

GAS『DriveApp cannot be used in this context』の原因と対処法

DriveAppが許可されない実行文脈(カスタム関数、シンプルトリガー、クライアント側JS、未認可の実行主体など)でDrive操作を呼んだときに発生。実行“入口”を見直し、インストール型トリガーやサーバ側関数へ寄せ、適切なスコープと実行主体で再デプロイするのが基本方針。

エラーの意味と発生条件(まず押さえる)

・セルのカスタム関数(=MYFUNC())は認可が必要なサービスを禁止

・シンプルトリガー(onOpen/onEdit/onInstall 等)は権限制限下で実行

・HtmlServiceのクライアントJSや外部ページからDriveAppを直接呼べない

・ウェブアプリ/実行API/アドオンでも、実行主体やスコープ不備で失敗

・共有ドライブ権限・所有者変更・ゴミ箱など周辺条件の取り違えも誘因

最短の切り分け(実行文脈を確定する)

・どこから呼んだか:セル/トリガー/ボタン/ウェブアプリ/外部HTTP

・誰の権限で走っているか:Session.getEffectiveUserで確認

・デプロイの種類とバージョン:最新バージョンか、実行主体は適切か

カスタム関数ではDriveAppを使わない(設計の分離)

・取得・作成などのI/Oはボタン/メニュー/定期実行に移し、結果だけをシートへ反映

・セルは“表示専用”、I/Oは“イベント駆動バッチ”に分担

サンプル:I/Oをインストール型トリガーへ移す

// ❌ カスタム関数内でDriveAppを呼ぶ(禁止文脈)
function LIST_FILES(folderId) {
  // return DriveApp.getFolderById(folderId).getFiles(); // ここでエラー
}

// ✅ メニューや手動実行・時間トリガーで動かす
function listFilesToSheet(folderId, sheetId, sheetName) {
  const sh = SpreadsheetApp.openById(sheetId).getSheetByName(sheetName);
  const it = DriveApp.getFolderById(folderId).getFiles();
  const out = [];
  while (it.hasNext()) {
    const f = it.next();
    out.push([f.getName(), f.getId(), f.getUrl(), f.getSize()]);
  }
  sh.clearContents();
  if (out.length) sh.getRange(1,1,out.length,4).setValues(out);
}

function onOpen() {
  SpreadsheetApp.getUi().createMenu('DriveOps')
    .addItem('フォルダ一覧を書き出す', 'runList')
    .addToUi();
}
function runList() {
  listFilesToSheet('YOUR_FOLDER_ID','YOUR_SHEET_ID','Data');
}

シンプルトリガー→インストール型トリガーに置換

・onEdit/onOpenなど“simple”は権限が弱い

・ScriptApp.newTriggerで“installed”に切替え、作成者が認可を通す

function job(e) {
  // DriveApp利用OK(トリガー作成者の認可で実行)
  const name = DriveApp.getFileById('YOUR_FILE_ID').getName();
  const sh = SpreadsheetApp.getActive().getSheetByName('Log');
  sh.appendRow([new Date(), name, e?.range?.getA1Notation() || '']);
}

function installOnEdit() {
  ScriptApp.newTrigger('job')
    .forSpreadsheet(SpreadsheetApp.getActive())
    .onEdit()
    .create();
}

クライアント側からはgoogle.script.runでサーバへ委譲

・ブラウザJSはDriveAppに触れない。サーバ関数がDrive操作を担当

// Code.gs
function createFileInFolder(folderId, name, content) {
  const file = DriveApp.getFolderById(folderId).createFile(name, content, MimeType.PLAIN_TEXT);
  return { id: file.getId(), url: file.getUrl() };
}
function doGet(){ return HtmlService.createHtmlOutputFromFile('index'); }

<!-- index.html -->
<input id="name" placeholder="file.txt">
<button onclick="run()">作成</button>
<script>
function run(){
  const n = document.getElementById('name').value || 'file.txt';
  google.script.run
    .withSuccessHandler(res => alert('作成: ' + res.url))
    .withFailureHandler(err => alert('失敗: ' + (err && err.message)))
    .createFileInFolder('YOUR_FOLDER_ID', n, 'hello');
}
</script>

ウェブアプリ/実行API:実行主体・スコープ・再デプロイ

・「自分として実行」or「アクセスするユーザーとして実行」を要件で選択

・Driveスコープが必要な変更後は“新バージョンとしてデプロイ”し再同意

・TestingとProductionのURL混在に注意(古いURLを叩かない)

共有ドライブ/権限のすれ違いを点検

・対象が共有ドライブなら実行主体に適切ロールが必須(閲覧のみでは作成不可)

・openByIdにURLではなく“IDのみ”を渡す、ゴミ箱・所有者変更も確認

function diagDrive(targetId) {
  const ctx = {
    effective: Session.getEffectiveUser().getEmail(),
    active: Session.getActiveUser().getEmail()
  };
  try {
    const f = DriveApp.getFileById(targetId);
    console.log('OK', { ctx, name: f.getName(), url: f.getUrl() });
    return true;
  } catch (e) {
    console.error('Drive diag failed', { ctx, msg: e.message });
    throw e;
  }
}

外部フロントからの直叩き禁止(CORS/認可の壁)

・外部サイト→GASをXHR直叩きし、サーバ側でDrive操作…は実装困難

・必要なら中継(Cloud Run/Functions)やGAS側UIで完結する設計へ

アドオン/組織ポリシーの制約とマニフェスト

・アドオンはappsscript.jsonで必要最小スコープを宣言し、管理者承認が必要

・組織で「信頼できるアプリ」制限があるとユーザー同意だけでは不可

NG→OK早見表(クイック修正)

・NG:カスタム関数でDriveApp → OK:ボタン/メニュー/時間トリガーでサーバ関数実行

・NG:onEdit(シンプル)でDrive → OK:インストール型onEditに置換

・NG:ブラウザJSでDrive → OK:google.script.runでサーバへ委譲

・NG:デプロイ更新/再同意なし → OK:新バージョンとしてデプロイ+認可完了

・NG:共有ドライブ権限不足 → OK:実行主体に適切ロール付与

・NG:openByIdにURL → OK:純粋なファイルIDを渡す

チェックリスト(導入順)

・実行“入口”はDriveApp可の文脈か(installed trigger / server-side)

・実行主体は要件どおりか(effectiveUser確認)

・Driveスコープで再デプロイし、同意を取り直したか

・共有ドライブや所有者・ゴミ箱など周辺権限を満たしているか

・クライアントはgoogle.script.runのみでサーバへ委譲しているか

・診断関数(diagDrive)で最小再現を確認し、ログを残しているか