GAS『Syntax Error: Unexpected Token』の原因と対処法

GAS『Syntax Error: Unexpected Token』の原因と対処法

Google Apps Script(V8)の構文解析で「その位置に現れてはいけない文字/記号が来た」と判断されると発生する。波括弧や括弧の不一致、カンマの欠落・余分、全角記号やスマートクォートの混入、JSONとオブジェクトの取り違え、awaitの誤用、import/exportの誤解などが典型。エラー行周辺を最小再現に縮め、トークン(記号)の前後関係を正す。

エラーメッセージの読み方と最短の切り分け

・「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(トップレベル関数)