GAS『Syntax Error: Unexpected Token』の原因と対処法
- 作成日 2025.10.23
- その他
Google Apps Script(V8)の構文解析で「その位置に現れてはいけない文字/記号が来た」と判断されると発生する。波括弧や括弧の不一致、カンマの欠落・余分、全角記号やスマートクォートの混入、JSONとオブジェクトの取り違え、awaitの誤用、import/exportの誤解などが典型。エラー行周辺を最小再現に縮め、トークン(記号)の前後関係を正す。
目次
- 1. エラーメッセージの読み方と最短の切り分け
- 2. 括弧・波括弧の不一致((), {}, [])
- 3. カンマの欠落・余分(オブジェクト/配列リテラル)
- 4. JSONとJSオブジェクトの取り違え(キーにクォート)
- 5. 全角記号・スマートクォート・不可視文字の混入
- 6. テンプレートリテラルとバッククォートのミス
- 7. async/awaitの誤用(GASは同期API中心)
- 8. import/exportの誤解(Apps Scriptはバンドル済みの単一グローバル)
- 9. アロー関数・オブジェクト即時返却の () / {} ミス
- 10. 正規表現リテラルのスラッシュ/エスケープ崩れ
- 11. HTML内の がJSを途中で終了させる(HTMLサービス)
- 12. Trailing comma の非対応環境/記法の混在(古いサンプル流用)
- 13. 診断テンプレ:該当行の前後をログで最小再現
- 14. CI的な防止策(整形と静的チェック)
- 15. よくあるNG→OK早見表
エラーメッセージの読み方と最短の切り分け
・「Unexpected token ‘}’」「Unexpected token ‘,’」「Unexpected identifier」「Unexpected string」など、どの記号/要素が想定外かを示す。
・直前の行が真犯人のことが多い(セミコロンやカンマ不足/括弧閉じ忘れ)。
・まず該当行の前後10~20行を別ファイルにコピーし、最小再現に縮めてから修正する。
括弧・波括弧の不一致((), {}, [])
// NG:関数ブロック } が不足
function main() {
const a = 1;
if (a > 0) {
Logger.log(a);
// ← ここで終わっていて } が足りない → Unexpected token 'EOF'
[/code]
[code]
// OK:すべての括弧を対応させる
function main() {
const a = 1;
if (a > 0) {
Logger.log(a);
}
}
// NG:プロパティ間のカンマ不足
const cfg = {
sheetId: 'xxx'
range: 'A1' // ← 前行にカンマが無い → Unexpected token 'range'
};カンマの欠落・余分(オブジェクト/配列リテラル)
// NG:プロパティ間のカンマ不足
const cfg = {
sheetId: 'xxx'
range: 'A1' // ← 前行にカンマが無い → Unexpected token 'range'
};
// OK:正しい区切り
const cfg = {
sheetId: 'xxx',
range: 'A1'
};JSONとJSオブジェクトの取り違え(キーにクォート)
// NG:JSONを期待するAPIにJSオブジェクトをそのまま文字列化していない
const body = { values: [['A','B']] };
UrlFetchApp.fetch('https://api', { method: 'post', payload: body }); // ← Unexpected token になりうる
// OK:JSON化して送る(ヘッダも)
const body = { values: [['A','B']] };
UrlFetchApp.fetch('https://api', {
method: 'post',
contentType: 'application/json',
payload: JSON.stringify(body)
});
// JSON文字列の例(キーはダブルクォート必須)
const json = '{"sheetId":"xxx","range":"A1"}';全角記号・スマートクォート・不可視文字の混入
// NG:全角カンマやスマートクォート
const msg = ‘hello’; // ← U+2018/U+2019
const ary = [1,2,3]; // ← 全角カンマ → Unexpected token
// OK:ASCIIのシングル/ダブルクォートと半角記号のみ
const msg = 'hello';
const ary = [1, 2, 3];テンプレートリテラルとバッククォートのミス
// NG:バッククォート閉じ忘れ
const s = `id=${Session.getActiveUser().getEmail(); // ← ` が足りない
// OK
const s = `id=${Session.getActiveUser().getEmail()}`;async/awaitの誤用(GASは同期API中心)
// NG:awaitはasync関数内でのみ使用可
function run() {
const res = await UrlFetchApp.fetch('https://example.com'); // Unexpected identifier 'await'
}
// OK:awaitを使わない or async関数にする(ただしGASの標準APIは同期)
function run() {
const res = UrlFetchApp.fetch('https://example.com');
Logger.log(res.getResponseCode());
}import/exportの誤解(Apps Scriptはバンドル済みの単一グローバル)
// NG:ES Modulesを直接書く
import { foo } from './lib.js'; // Unexpected token 'import'
export function main() {} // Unexpected token 'export'
// OK:同一プロジェクト内の別ファイルに定義し、グローバル関数として参照
// lib.gs
function foo() { return 'ok'; }
// Code.gs
function main() { Logger.log(foo()); }アロー関数・オブジェクト即時返却の () / {} ミス
// NG:オブジェクトを返したいが {} がブロック扱い
const mk = (x) => { id: x, ok: true }; // Unexpected token ':'
// OK:()で包む
const mk = (x) => ({ id: x, ok: true });正規表現リテラルのスラッシュ/エスケープ崩れ
// NG:/ をエスケープせずパス区切りと紛れる
const re = /https://example.com/; // Unexpected token ':' など
// OK:適切にエスケープ
const re = /https:\/\/example\.com/;HTML内の がJSを途中で終了させる(HTMLサービス)
<!-- NG:テンプレートHTML -->
<script>
const t = "</script>"; // ← ブラウザがここでscriptを閉じて残りが文字化け
</script>
<!-- OK:エスケープ or 分割 -->
<script><br />
const t = "<\/script>";<br />
</script>Trailing comma の非対応環境/記法の混在(古いサンプル流用)
// NG:関数引数の末尾カンマは環境差でエラー
function f(a, b,) { return a + b; } // Unexpected token ')'
// OK:引数末尾はカンマ無しに統一
function f(a, b) { return a + b; }診断テンプレ:該当行の前後をログで最小再現
/**
* 1) エラー行の前後だけを抜き出し
* 2) 変数宣言・括弧・カンマを最小に削る
* 3) 通るところまで戻して差分を見る
*/
function minimal() {
const cfg = {
sheetId: 'xxx',
range: 'A1'
};
// ここに問題行を最小化して貼る
}CI的な防止策(整形と静的チェック)
・保存時フォーマッタ(ESLint + Prettier)を有効にして括弧やカンマの崩れを自動修正。
・プロパティ名や引用符のルールをプロジェクトで固定(single quotes、no-unexpected-multiline など)。
・外部から貼ったコードはまずUTF-8/ASCII化(全角・不可視文字を除去)。
よくあるNG→OK早見表
// 1) カンマ不足
const a = { x:1 y:2 }; // NG
const a = { x:1, y:2 }; // OK
// 2) オブジェクト即時返却
[1,2].map(n => { value: n }); // NG
[1,2].map(n => ({ value: n })); // OK
// 3) 文字列の引用符
const s = "he said "hi""; // NG
const s = "he said \"hi\""; // OK
// 4) JSONとJS
JSON.parse({a:1}); // NG(文字列ではない)
JSON.parse('{"a":1}'); // OK
// 5) import/export
export function run(){} // NG(GASでは不可)
function run(){} // OK(トップレベル関数)-
前の記事
GAS『OAuth2 Authorization』の原因と対処法 2025.10.22
-
次の記事
GAS『Exceeded Maximum Email Recipients per Day』の原因と対処法 2025.10.23
コメントを書く