- クラウドサービス
GASエラー一覧|メッセージ別の原因と対処コード早見表
GAS(Google Apps Script)のエラーは、メッセージの先頭語を見れば「どの層で起きたか」が決まり、調べる場所が1か所に絞れます。 TypeError・ReferenceError・SyntaxError・RangeError は JavaScript のエンジン(V8)が出すもので自分のコードの中に原因があり、それ以外は Google のサービス側が返す例外で、権限・割り当て・管理者設定のどれかに原因があります。
この記事では、エラーメッセージの原文で引ける早見表42件に、日本語訳・原因・直すコードを付けました。あわせて公式の割り当て表、6分の上限を越える処理を分割して続きから再開するコード、トリガーの種類による権限の違い、null と undefined の出どころの判断表を1ページにまとめています。
確認環境(確認日 2026-10-05):JavaScript 由来のメッセージ文は Node.js 22.22.2(GAS の V8 ランタイムと同じ V8 エンジン)で実際に発生させたものです。エンジンの版によって細かな文言が異なる場合があります。判断の流れ図は Mermaid(@mermaid-js/mermaid-cli 12.0.0)で描画できることを確かめました。Google のサービスが返すメッセージ・割り当ての数値・トリガーの制限は、Google Apps Script 公式ドキュメント(末尾の参考)の記載です。
この記事の結論
| メッセージの先頭 | どの層で起きたか | 最初に見る場所 |
|---|---|---|
| SyntaxError | 書き方(保存・実行の前に見つかる)。JSON.parse の中身が JSON でないときも出る | 表示された行の直前のかっこ・引用符・カンマ |
| ReferenceError | 名前(変数・関数が見つからない) | つづり、宣言の位置、別ファイルの関数名 |
| TypeError | 値(null・undefined・型違いにメソッドを呼んだ) | reading '○○' の○○の左側にある変数の中身 |
| RangeError | 値の範囲(無限の再帰、無効な日付) | 再帰の終了条件、Date の中身 |
| 上記以外(Google のサービスが返す例外) | 権限・割り当て・管理者設定・サーバー側 | 承認の状態、割り当て表、トリガーの種類 |
- TypeError の多くは「null か undefined にドットを付けた」。reading のあとの名前を手がかりに、その左側の変数を調べます。
- 1回の実行は6分まで。長い処理は「途中の位置を保存 → 時間主導型トリガーで続きを予約」の定型で分割します。
- 単純トリガー(onEdit など)は承認が必要なサービスを呼べず、30秒まで。メール送信などはインストール型トリガーに切り替えます。
GASエラーの早見表(メッセージ原文で引く)
ブラウザのページ内検索(Ctrl+F)にエラーメッセージの一部を貼り付けて探してください。変数名や関数名の部分(sheeet、s.getRnage など)は、手元の名前に読み替えます。
TypeError(値が想定と違う)
| No. | エラーメッセージ原文 | 日本語訳 | 主な原因 | 直すコード |
|---|---|---|---|---|
| 1 | TypeError: Cannot read properties of null (reading 'getRange') |
null のプロパティ getRange を読めません | getSheetByName に渡したシート名が存在せず null が返った | if (sheet === null) throw new Error("シート名を確認"); |
| 2 | TypeError: Cannot read properties of undefined (reading 'range') |
undefined のプロパティ range を読めません | onEdit(e) などをエディタから手動実行し、e が渡されていない | if (!e || !e.range) return; |
| 3 | TypeError: Cannot read properties of undefined (reading '1') |
undefined の 1 番目を読めません | 二次元配列で、存在しない行を指定した(rows[0] が無い) | if (rows.length > 0) { rows[0][1]; } |
| 4 | TypeError: Cannot set properties of undefined (setting 'name') |
undefined のプロパティ name に代入できません | 変数を宣言しただけで、オブジェクトを入れていない | let o = {}; o.name = "a"; |
| 5 | TypeError: s.getRnage is not a function |
s.getRnage は関数ではありません | メソッド名のつづり違い、または別のクラスのメソッドを呼んだ | 公式リファレンスで名前を確認(getRange) |
| 6 | TypeError: v.map is not a function |
v.map は関数ではありません | getValue(単一の値)の結果に配列用のメソッドを使った | sheet.getRange("A1:A10").getValues().map(r => r[0]) |
| 7 | TypeError: v.split is not a function |
v.split は関数ではありません | セルの値が数値や日付で、文字列ではない | String(v).split(",") |
| 8 | TypeError: "2026/10/05".getTime is not a function |
文字列に getTime はありません | 日付のつもりの値が文字列だった | new Date(v).getTime() |
| 9 | TypeError: o is not iterable |
o は繰り返しできません | オブジェクトを for...of で回した | for (const [k, v] of Object.entries(o)) {} |
| 10 | TypeError: undefined is not iterable |
undefined は繰り返しできません | 配列の分割代入の右辺が undefined | const [a] = list ?? []; |
| 11 | TypeError: Cannot destructure property 'range' of 'undefined' as it is undefined. |
undefined から range を取り出せません | 引数の分割代入で、引数そのものが渡されていない | function f({ range } = {}) {} |
| 12 | TypeError: Assignment to constant variable. |
定数に再代入しました | const で宣言した変数に代入した | 値を変える変数は let で宣言 |
| 13 | TypeError: f is not a constructor |
f はコンストラクタではありません | アロー関数や通常の値に new を付けた | class か function で定義して new する |
| 14 | TypeError: Class constructor A cannot be invoked without 'new' |
クラスは new なしで呼べません | クラスを関数のように呼んだ | const a = new A(); |
| 15 | TypeError: Converting circular structure to JSON |
循環した構造は JSON にできません | 自分自身を参照するオブジェクトを JSON.stringify に渡した | 必要な項目だけ取り出して JSON.stringify({ id: o.id }) |
ReferenceError(名前が見つからない)
| No. | エラーメッセージ原文 | 日本語訳 | 主な原因 | 直すコード |
|---|---|---|---|---|
| 16 | ReferenceError: sheeet is not defined |
sheeet は定義されていません | 変数名・関数名のつづり違い、宣言したブロックの外で使った、ライブラリの識別子が違う | 宣言と同じつづりにそろえる。ブロックの外で使うなら外側で let 宣言 |
| 17 | ReferenceError: Cannot access 'x' before initialization |
初期化の前に x へアクセスできません | let・const の宣言より上の行で使った | 宣言を、使う行より上へ移す |
SyntaxError(書き方・JSON の形)
| No. | エラーメッセージ原文 | 日本語訳 | 主な原因 | 直すコード |
|---|---|---|---|---|
| 18 | SyntaxError: Unexpected token ';' |
予期しない記号 ; があります | オブジェクトの中にセミコロン、かっこの対応ずれ | const a = { b: 1 }; |
| 19 | SyntaxError: Unexpected end of input |
入力が途中で終わりました | 波かっこ・バッククォートの閉じ忘れ | function f() { if (true) { } } |
| 20 | SyntaxError: Identifier 'a' has already been declared |
a は宣言済みです | 同じ名前を let・const で2回宣言した(別ファイルの最上位も同じ空間) | 名前を変えるか、2回目は宣言を外して代入だけにする |
| 21 | SyntaxError: missing ) after argument list |
引数のあとの ) がありません | 丸かっこの閉じ忘れ、文字列の連結で + が抜けた | console.log("a"); |
| 22 | SyntaxError: Invalid or unexpected token |
無効な文字があります | 引用符の閉じ忘れ、全角のセミコロンや引用符が混ざった | 全角記号を半角に直す、引用符を閉じる |
| 23 | SyntaxError: Missing initializer in const declaration |
const に初期値がありません | const を値なしで宣言した | const a = 1; または let a; |
| 24 | SyntaxError: Cannot use import statement outside a module |
モジュールの外で import は使えません | import 文を書いた。公式の V8 ランタイムの説明では import/export に対応していない | 別ファイルの関数はそのまま呼ぶ。外部コードはライブラリとして追加 |
| 25 | SyntaxError: await is only valid in async functions and the top level bodies of modules |
await は async 関数の中で使います | async を付けていない関数で await を書いた | async function f() { await g(); } |
| 26 | SyntaxError: Unexpected token '<', "<!DOCTYPE html>" is not valid JSON |
先頭が山かっこで、JSON ではありません | JSON.parse に HTML(エラーページやログイン画面)が渡った | getResponseCode() と getContentText() の先頭を確かめてから parse |
| 27 | SyntaxError: Unexpected end of JSON input |
JSON が途中で終わっています | 空文字を JSON.parse に渡した | const data = text ? JSON.parse(text) : {}; |
| 28 | SyntaxError: "undefined" is not valid JSON |
undefined は JSON ではありません | 値の入っていない変数を JSON.parse に渡した(e.postData が無い など) | if (e && e.postData) JSON.parse(e.postData.contents); |
RangeError(値の範囲)
| No. | エラーメッセージ原文 | 日本語訳 | 主な原因 | 直すコード |
|---|---|---|---|---|
| 29 | RangeError: Maximum call stack size exceeded |
呼び出しの深さが上限を越えました | 終了条件のない再帰、自分自身を呼ぶ関数名の取り違え | 再帰に終了条件を入れる、ループに書き換える |
| 30 | RangeError: Invalid time value |
無効な時刻の値です | 無効な Date(Invalid Date)に toISOString などを呼んだ | if (!isNaN(d.getTime())) d.toISOString(); |
| 31 | RangeError: Invalid array length |
配列の長さが無効です | new Array に負の数や小数を渡した | 長さを計算した値が0以上の整数か確かめる |
Googleのサービスが返す例外(権限・割り当て・管理者設定)
公式のトラブルシューティングに載っているメッセージです。山かっこの部分には、実際の操作名やURLが入ります。
| No. | エラーメッセージ原文 | 日本語訳 | 主な原因(公式の説明) | 対処 |
|---|---|---|---|---|
| 32 | Service invoked too many times: <action name> |
サービスの呼び出しが多すぎます | その操作の1日の割り当てを越えた | 下の割り当て表で上限を確認し、回数を減らす(まとめて処理する) |
| 33 | Server not available. / Server error occurred, please try again. |
サーバーを利用できません/もう一度試してください | Google 側の一時的な不調、メッセージを伴わないスクリプトのエラー | 時間をおいて再実行。続く場合はデバッグで箇所を絞る |
| 34 | Authorization is required to perform that action. |
その操作には承認が必要です | 承認していないサービスを、トリガーやサービスとして実行した(承認画面を出せない場面) | エディタで任意の関数を1回実行して承認する。新しいサービスを足したら承認し直す |
| 35 | Access denied: DriveApp |
DriveApp へのアクセスが拒否されました | 管理者がドメインの Drive API を無効にしている | 管理者に有効化を依頼する |
| 36 | The domain policy has disabled third-party Drive apps |
ドメインのポリシーで外部の Drive アプリが無効です | 同上 | 同上 |
| 37 | The script does not have permission to get the active user's identity. |
実行中のユーザーの情報を得る権限がありません | 承認が完全でない状態(トリガーなど)で Session.getActiveUser などを呼んだ | 完全に承認された実行(インストール型トリガーなど)で呼ぶ |
| 38 | Library is missing. |
ライブラリが見つかりません | 利用の集中などでライブラリを読み込めなかった | コードを自分のスクリプトにコピーする、不要なライブラリを外す |
| 39 | Error occurred due to a missing library version or a deployment version. Error code Not_Found |
ライブラリまたはデプロイの版が見つかりません | デプロイやライブラリが、削除された版を指している | デプロイを編集して存在する版を選ぶ。ライブラリの版を更新する |
| 40 | UrlFetch calls to <URL> are not permitted by your admin |
そのURLへの UrlFetch は管理者に許可されていません | 管理者が外部ドメインを許可リストで制御している | 管理者に対象URLの追加を依頼する |
| 41 | Calling .constructor on a function is prohibited |
関数の constructor の呼び出しは禁止です | 文字列から関数を動的に作る書き方(安全のため止められる) | globalThis を使う。関数は通常の宣言で定義する |
| 42 | Permissions policy violation |
権限ポリシーに反しています | HtmlService の画面からカメラ・マイクなど制限された Web API を使った | その機能は外部ドメインに置き、window.postMessage でやり取りする |
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「GASエラー一覧|メッセージ別の原因と対処コード早見表」
先頭語で原因の層が決まる理由
GAS のコードは、V8 という JavaScript エンジンの上で動き、そこから SpreadsheetApp や GmailApp といった Google のサービスを呼び出します。エラーを出す場所が2つあるので、メッセージの形も2系統になります。
flowchart TD
A[エラーメッセージの先頭を見る] --> B{SyntaxError か}
B -- はい --> B1[書き方の層: かっこ 引用符 全角記号<br>または JSON.parse に渡した文字列]
B -- いいえ --> C{ReferenceError か}
C -- はい --> C1[名前の層: つづり 宣言の位置]
C -- いいえ --> D{TypeError か RangeError か}
D -- はい --> D1[値の層: null undefined 型違い 無効な日付]
D -- いいえ --> E[サービスの層: 承認 割り当て<br>トリガーの種類 管理者設定]
| 層 | いつ見つかるか | 自分のコードを直せば解決するか |
|---|---|---|
| 書き方(SyntaxError) | 保存時・実行の開始前。JSON.parse 由来は実行中 | はい |
| 名前(ReferenceError) | その行に到達したとき | はい |
| 値(TypeError・RangeError) | その行に到達したとき。データ次第で出たり出なかったりする | はい(値の確認を足す) |
| サービス(Google が返す例外) | サービスを呼んだとき | コードだけでは解決しないことがある(承認・割り当て・管理者設定) |
値の層のエラーは「昨日は動いたのに今日は止まる」形で現れます。 コードは同じでも、シート名が変わった、空行が増えた、APIの応答が変わった、といったデータの変化で起きるためです。
nullとundefinedの出どころを見分ける
TypeError の No.1〜3 は、メッセージの「of null」と「of undefined」で出どころが分かれます。GAS のメソッドは「見つからない」を null で返すものが多く、JavaScript 自体は「渡されていない・存在しない」を undefined で表します。
| メッセージ | 出どころ | 典型例 | 確かめ方 |
|---|---|---|---|
| of null | GAS のメソッドが「無い」を返した | getSheetByName(公式:その名前のシートが無ければ null)、getProperty(公式:キーが無ければ null) | 渡した名前・キーを console.log で表示し、実物と1文字ずつ比べる(全角・半角、前後の空白) |
| of undefined | 引数が渡されていない | onEdit(e)・doPost(e) をエディタから手動実行した | 実行方法を確認。トリガーから呼ばれたときだけ e が入る |
| of undefined | 配列の範囲外 | 0行の getValues 結果に rows[0][1]、最終行を越えた添字 | rows.length と添字を表示する |
| of undefined | オブジェクトに無いキー | APIの応答に想定した項目が無い、編集前が空のセルの e.oldValue(公式:空だったセルでは undefined) | Object.keys で実際のキーを表示する |
次のコードは、GAS のメソッドと同じ値(null・undefined)を返す代役を使って、メッセージの違いを再現したものです。
// null と undefined の出どころを見分ける(GAS の戻り値を同じ値の代役で再現)
const ss = { getSheetByName: (n) => (n === "売上" ? { getName: () => n } : null) };
const props = { getProperty: (k) => null };
function onEdit(e) {
return e.range; // エディタから手動実行すると e は undefined
}
const tests = {
"無いシート名": () => ss.getSheetByName("売り上げ").getName(),
"無いプロパティ": () => props.getProperty("API_KEY").trim(),
"引数なしで onEdit": () => onEdit(),
"範囲外の行": () => [["a", "b"]][1][0],
"無いキー": () => ({ user: {} }).user.profile.name,
};
for (const [label, fn] of Object.entries(tests)) {
try { fn(); } catch (err) { console.log(label, "=>", err.name + ": " + err.message); }
}
実行結果(Node.js 22):
無いシート名 => TypeError: Cannot read properties of null (reading 'getName')
無いプロパティ => TypeError: Cannot read properties of null (reading 'trim')
引数なしで onEdit => TypeError: Cannot read properties of undefined (reading 'range')
範囲外の行 => TypeError: Cannot read properties of undefined (reading '0')
無いキー => TypeError: Cannot read properties of undefined (reading 'name')
直した形です。null は「=== null で分けて、分かりやすいメッセージで止める」、undefined は「先頭で確認して戻る」か「?. と ?? で既定値にする」と使い分けます。
const sheet = ss.getSheetByName("売り上げ");
console.log(sheet === null ? "シートが見つかりません(名前を確認)" : sheet.getName());
function onEditSafe(e) {
if (!e || !e.range) { console.log("手動実行のため終了(トリガーから呼ばれたときだけ動く)"); return; }
return e.range;
}
onEditSafe();
console.log(({ user: {} }).user.profile?.name ?? "(名前なし)");
シートが見つかりません(名前を確認)
手動実行のため終了(トリガーから呼ばれたときだけ動く)
(名前なし)
公式の割り当て表(一般アカウントとWorkspace)
割り当てを越えると、スクリプトは例外を出して止まります。公式ページ「Quotas for Google Services」の記載から、よく当たる項目を抜き出しました。公式ページには、割り当ては予告なく変わることがあると書かれているので、運用前に最新の表を確認してください。
1日あたりの割り当て
| 項目 | 一般アカウント(gmail.com) | Google Workspace |
|---|---|---|
| トリガーの合計実行時間 | 90分/日 | 6時間/日 |
| メールの宛先数 | 100/日 | 1,500/日 |
| メールの読み書き(送信を除く) | 20,000/日 | 50,000/日 |
| URL Fetch の呼び出し | 20,000/日 | 100,000/日 |
| プロパティの読み書き | 50,000/日 | 500,000/日 |
| カレンダーの予定の作成 | 5,000/日 | 10,000/日 |
| ドキュメントの作成 | 250/日 | 1,500/日 |
| スプレッドシートの作成 | 250/日 | 3,200/日 |
1回あたり・1件あたりの上限
| 項目 | 一般アカウント | Google Workspace |
|---|---|---|
| スクリプトの実行時間 | 6分/回 | 6分/回 |
| カスタム関数の実行時間 | 30秒/回 | 30秒/回 |
| 同時実行(ユーザーあたり) | 30 | 30 |
| トリガーの数 | 20/ユーザー/スクリプト | 20/ユーザー/スクリプト |
| メール1通の宛先数 | 50 | 50 |
| メール本文のサイズ | 200 KB | 400 KB |
| メールの添付の合計サイズ | 25 MB | 25 MB |
| プロパティの値のサイズ | 9 KB/値 | 9 KB/値 |
| プロパティの合計サイズ | 500 KB/ストア | 500 KB/ストア |
| URL Fetch の応答サイズ | 50 MB/回 | 50 MB/回 |
| URL Fetch の URL の長さ | 2 KB/回 | 2 KB/回 |
表計算ソフトに貼れるタブ区切り版です。
項目 一般アカウント Google Workspace
スクリプトの実行時間 6分/回 6分/回
カスタム関数の実行時間 30秒/回 30秒/回
トリガーの合計実行時間 90分/日 6時間/日
トリガーの数 20/ユーザー/スクリプト 20/ユーザー/スクリプト
メールの宛先数 100/日 1,500/日
メール1通の宛先数 50 50
URL Fetch の呼び出し 20,000/日 100,000/日
プロパティの読み書き 50,000/日 500,000/日
1回の実行時間(6分)は、Workspace でも同じです。Workspace で広がるのは1日あたりの量なので、6分を越える処理はアカウントの種類にかかわらず分割が必要になります。
6分の上限を越える処理を分割して再開するコード
考え方は3つです。(1) 上限より手前で自分から止まる、(2) 次に処理する位置をスクリプトプロパティに保存する、(3) 時間主導型トリガーで続きを予約する。
// 6分の上限を越える処理を、分割して続きから再開する定型
const LIMIT_MS = 4.5 * 60 * 1000; // 6分より手前で自分から止まる
const KEY = "RESUME_ROW"; // 次に処理する行番号を保存するキー
const HANDLER = "processInChunks"; // 再開時に呼ぶ関数名
function processInChunks() {
const started = Date.now();
const props = PropertiesService.getScriptProperties();
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("データ");
if (sheet === null) throw new Error("シート「データ」が見つかりません");
const lastRow = sheet.getLastRow();
let row = Number(props.getProperty(KEY)) || 2; // 保存が無ければ2行目から
deleteResumeTriggers_(); // 前回の再開用トリガーを片付ける
while (row <= lastRow) {
if (Date.now() - started > LIMIT_MS) { // 時間切れが近い → 続きを予約して終了
props.setProperty(KEY, String(row));
ScriptApp.newTrigger(HANDLER).timeBased().after(60 * 1000).create();
console.log("中断: 次は " + row + " 行目から");
return;
}
processRow_(sheet, row); // 1行ぶんの処理
row++;
}
props.deleteProperty(KEY); // 最後まで終わったら目印を消す
console.log("完了: " + (lastRow - 1) + " 行");
}
function processRow_(sheet, row) {
const value = sheet.getRange(row, 1).getValue();
sheet.getRange(row, 2).setValue("処理済み: " + value);
}
function deleteResumeTriggers_() {
ScriptApp.getProjectTriggers().forEach(function (t) {
if (t.getHandlerFunction() === HANDLER) ScriptApp.deleteTrigger(t);
});
}
使っているメソッド(getProperty・setProperty・deleteProperty、newTrigger、timeBased、after、getProjectTriggers、getHandlerFunction、deleteTrigger、getSheetByName、getLastRow)は、公式リファレンスで名前・引数・戻り値を確認しています。構文は node --check で確かめました。
流れそのものは、GAS のサービスを最小限の代役に置き換え、1行の処理に1分かかる10行のデータを与えて Node.js で動かして確かめました。4.5分を越えたところで止まり、2回目で残りを終えています。
--- 1 回目の実行
中断: 次は 7 行目から
保存値={"RESUME_ROW":"7"} 再開用トリガー=1
--- 2 回目の実行
完了: 10 行
保存値={} 再開用トリガー=0
10 行に書き込み
| 設計のポイント | 理由 |
|---|---|
| 上限を4.5分に置く | 1行の処理やトリガー作成の時間を見込み、6分に対して余裕を残す |
| 再開のたびに前回のトリガーを消す | トリガーは20/ユーザー/スクリプトまで。消さないと積み上がる |
| 行番号を文字列で保存する | プロパティの値は文字列(getProperty の戻り値は String) |
| 終わったら目印を消す | 次に最初から実行したとき、途中から始まらないようにする |
| トリガーの合計時間も数える | 再開ぶんはトリガーの合計実行時間(90分/日・6時間/日)に入る |
あわせて、1行ずつの getValue/setValue を、getValues/setValues で範囲ごとにまとめると、サービスの呼び出し回数が減り、分割そのものが要らなくなることもあります。setValues に渡す二次元配列は、範囲と同じ行数・列数にそろえます。
単純トリガーとインストール型トリガーの違い
「onEdit の中でメールを送ると止まる」「onOpen が動かない」は、トリガーの種類による制限が原因のことが多い部分です。公式の「Simple Triggers」「Installable Triggers」の記載を表にしました。
| 比べる点 | 単純トリガー | インストール型トリガー |
|---|---|---|
| 作り方 | onOpen(e)・onEdit(e)・onSelectionChange(e)・onInstall(e) という決まった名前の関数を書くだけ | エディタのトリガー画面、または ScriptApp.newTrigger で作る |
| 承認が必要なサービス(Gmail など) | 呼べない | 呼べる |
| 実行時間 | 30秒まで | 1回の実行時間の上限(6分)まで |
| 触れるファイル | 紐づいたファイルだけ(他のファイルにはアクセスできない) | 承認した範囲 |
| 誰の権限で動くか | 操作したユーザー(ユーザーの特定は制限される) | トリガーを作った人のアカウント |
| 種類 | 開く・編集・選択の変更・インストール | 時間主導・開く・編集・変更・フォーム送信・カレンダーの予定 |
| 閲覧のみ・コメントのみで開いたとき | 動かない | 動かない |
| スクリプトやAPIによる変更 | 動かない(人の操作で動く) | 動かない(人の操作で動く) |
doGet(e)・doPost(e) も決まった名前の関数ですが、公式では上の制限の対象外とされています。
どちらを使うかの判断表
| やりたいこと | 選ぶもの |
|---|---|
| 開いたときにメニューを追加する | 単純トリガー(onOpen) |
| 編集されたセルの隣に時刻を書く | 単純トリガー(onEdit) |
| 編集されたらメールやチャットで知らせる | インストール型の編集トリガー |
| 編集内容を別のスプレッドシートへ写す | インストール型の編集トリガー |
| 毎朝9時に集計する | インストール型の時間主導型トリガー |
| フォームの回答を受けて処理する | インストール型のフォーム送信トリガー |
| 行や列の追加・削除を検知する | インストール型の変更トリガー |
時間主導型トリガーをコードで作る例です(公式の書き方)。everyMinutes に指定できるのは 1・5・10・15・30 です。
function createTriggers() {
ScriptApp.newTrigger("dailyReport").timeBased().everyHours(6).create();
ScriptApp.newTrigger("dailyReport")
.timeBased()
.onWeekDay(ScriptApp.WeekDay.MONDAY)
.atHour(9)
.create();
}
止まった場所を早く見つける手順
| 順番 | すること | 使うもの |
|---|---|---|
| 1 | メッセージの先頭語で層を決める | この記事の結論の表 |
| 2 | 行番号の行で、reading '○○' の左側の変数を表示する | console.log、Logger.log |
| 3 | トリガー経由の実行は「実行数」の画面でログを見る | エディタの実行数パネル |
| 4 | 値を1つずつ追う | デバッグ実行とブレークポイント |
| 5 | サービス側が疑わしいときは稼働状況を見る | Google Workspace ステータス ダッシュボード |
エラーを受け止めて、原因が分かるメッセージに言い換える書き方です。err.name に先頭語、err.message に本文が入ります。
try {
null.x;
} catch (err) {
console.log(err.name, "|", err.message, "|", err instanceof TypeError);
}
TypeError | Cannot read properties of null (reading 'x') | true
GAS では、catch の中で console.error(err.stack) を残し、利用者には throw new Error("シート「データ」が見つかりません") のように日本語で言い直すと、次に見る人が早く原因にたどり着けます。
確かめて分かったこと
- コード中の全角スペースは SyntaxError にならず、全角のセミコロンは SyntaxError(Invalid or unexpected token)になりました。 全角記号が原因のときは、行番号の行を打ち直すのが早道です。
- JSON.parse のメッセージは、渡した中身で3通りに分かれました。 HTML なら Unexpected token と先頭の文字、空文字なら Unexpected end of JSON input、undefined なら "undefined" is not valid JSON です。メッセージから「何が渡ったか」を逆算できます。
- 配列の範囲外は reading のあとに数字が出ます(reading '1' など)。数字が出たら添字、名前が出たらメソッドやプロパティ、と読み分けられます。
- 1回6分の上限は一般アカウントと Workspace で同じで、違うのは1日の合計(トリガーの合計実行時間が90分と6時間)でした。
- 分割と再開のコードは、再開用トリガーを消す処理を先頭に置くと、トリガーが積み上がらないことを代役つきの実行で確かめました(2回目の終了時にトリガー0件)。
- 公式のトラブルシューティングでは、複数の Google アカウントに同時にログインした状態はサポート対象外とされています。承認まわりで説明のつかない動きをするときは、シークレットウィンドウで1つのアカウントだけにして試します。
よくある質問
Q. GASの「TypeError: Cannot read properties of null」とは何ですか? A. null に対してメソッドやプロパティを使ったときに出るエラーです。GAS では getSheetByName に存在しないシート名を渡した場合が代表で、reading のあとに出る名前(getRange など)の左側にある変数が null になっています。
Q. nullとundefinedの違いは何ですか? A. GAS のメソッドが「見つからない」と答えたのが null、値がそもそも渡されていない・存在しないのが undefined です。of null ならシート名やキーを、of undefined なら実行方法(手動実行)や配列の範囲を確かめます。
Q. GASの実行時間の上限を越えて止まるときはどうすればよいですか? A. 処理を分割し、続きの位置を保存して時間主導型トリガーで再開します。1回の実行は6分までで、これは Workspace でも変わりません。先に getValues/setValues でまとめて読み書きし、呼び出し回数を減らすのも有効です。
Q. onEditでメールが送れないときは? A. 単純トリガーは承認が必要なサービスを呼べないため、インストール型の編集トリガーに切り替えます。関数名を onEdit 以外(例:notifyOnEdit)にして、トリガー画面から「編集時」で登録します。
Q. 「Authorization is required to perform that action」が出たときは? A. エディタで任意の関数を1回手動実行し、承認画面で許可します。トリガーやサービスとして動く場面では承認画面を出せないため、新しいサービスをコードに足したあとは手動実行での承認が必要です。
まとめ
- 先頭語で層が決まる:SyntaxError は書き方、ReferenceError は名前、TypeError・RangeError は値、それ以外は Google のサービス側(承認・割り当て・管理者設定)。
- TypeError は reading のあとの名前を手がかりに、その左側の変数を調べる。of null は GAS のメソッドの戻り値、of undefined は引数・配列の範囲・無いキー。
- getSheetByName と getProperty は、無いと null を返す。=== null で分けて、分かりやすいメッセージで止める。
- トリガー用の関数は先頭で e を確認する。エディタからの手動実行では e が渡されない。
- 1回の実行は6分、単純トリガーは30秒、カスタム関数は30秒。長い処理は位置を保存して時間主導型トリガーで再開する。
- 承認が必要な処理は、単純トリガーではなくインストール型トリガーで動かす。
- 割り当ての数値は変わることがあるので、公式の表で最新の値を確認する。
参考・出典
- Google Apps Script「Troubleshooting」 https://developers.google.com/apps-script/guides/support/troubleshooting
- Google Apps Script「Quotas for Google Services」 https://developers.google.com/apps-script/guides/services/quotas
- Google Apps Script「Simple Triggers」 https://developers.google.com/apps-script/guides/triggers
- Google Apps Script「Installable Triggers」 https://developers.google.com/apps-script/guides/triggers/installable
- Google Apps Script「Event Objects」 https://developers.google.com/apps-script/guides/triggers/events
- Google Apps Script「V8 Runtime Overview」 https://developers.google.com/apps-script/guides/v8-runtime
- Google Apps Script リファレンス「Class Spreadsheet」「Class Properties」「Class ScriptApp」 https://developers.google.com/apps-script/reference
本記事の内容は、ご自由にお使いください。
ご利用の際は、出典として本ページへのリンクを記載いただけますようお願いします。
(記載例)出典:株式会社RJC「GASエラー一覧|メッセージ別の原因と対処コード早見表」
株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。