GAS『Unexpected End of Script』の原因と対処法

GAS『Unexpected End of Script』の原因と対処法

JavaScriptの構文解析がファイルの末尾まで来ても閉じるべき記号やコメントが見つからないと発生。閉じ忘れ(}, ], ), “, ‘, `, */)、テンプレートリテラルや正規表現の崩れ、HTMLサービスでの誤終端、マージ時の欠落や不可視文字が主因。発生行の「直前」を疑い、最小再現で切り出して修正する。

エラーの意味と発生条件

「Unexpected end of script / input」は“終端記号の不足”を示す。

典型はブロック/配列/関数/クラス/try…catch…finallyの閉じ忘れ、コメントや文字列の未閉鎖、テンプレートリテラルのバッククォート不足。

スタックの最下段は末尾近くを指すが、真犯人は数行〜数十行手前にあることが多い。

最短で直す手順(実務向け)

1 行末の;ではなく、括弧とクォートの対応を確認。

2 直前20〜50行を別ファイルにコピーし、最小再現に削る。

3 コメントやテンプレート、正規表現の終端記号を点検。

4 HTMLサービスなら文字列のエスケープを確認。

5 フォーマッタを一度通し、整形で閉じ忘れ箇所を可視化。

括弧・波括弧・角括弧の閉じ忘れ

// NG: if ブロックを閉じ忘れ → Unexpected end of script
function main() {
  const rows = [1,2,3];
  if (rows.length > 0) {
    Logger.log(rows.join(','));
}

// OK
function main() {
  const rows = [1,2,3];
  if (rows.length > 0) {
    Logger.log(rows.join(','));
  }
}

文字列やテンプレートリテラルの未閉鎖

// NG: ' と " と ` の閉じ忘れ
const a = "hello;
const b = 'world;
const c = `id=${Session.getActiveUser().getEmail()}`; // ← OKの例

// OK
const a = "hello";
const b = 'world';
const c = `id=${Session.getActiveUser().getEmail()}`;

ブロックコメントや正規表現の終端崩れ

// NG: ブロックコメントを閉じていない
/*
  note: ここで */ を忘れるとファイル末尾までコメント扱い → Unexpected end of script

// OK
/*
  note
*/

// NG: 正規表現の / とエスケープが崩れている
const re = /https://example.com/;   // : で崩れる
// OK
const re = /https:\/\/example\.com/;

HTMLサービスでの 誤終端

<!-- NG: 文字列に </script> を素で書くとブラウザがタグ終端と解釈 -->
<script>
  const s = "</script>";
</script>

<!-- OK: スラッシュをエスケープ or 連結で分断 -->
<script>
  const s = "<\/script>";
  // または
  const s2 = "</scr" + "ipt>";
</script>

マージや貼り付け時の不可視文字・全角記号

// NG: 全角クォート・全角カンマが混ざると構文崩れ
const title = ‘通知’;  // U+2018/U+2019
const list = [1,2,3]; // 全角カンマ

// OK: ASCIIの記号のみ
const title = '通知';
const list = [1, 2, 3];

try/catch/finally の対応漏れ

// NG: finally を書いたが閉じていない
function run() {
  try {
    doWork();
  } catch (e) {
    Logger.log(e);
  finally {  // ← この後の } が足りない
    cleanup();
}

// OK
function run() {
  try {
    doWork();
  } catch (e) {
    Logger.log(e);
  } finally {
    cleanup();
  }
}

自動整形・静的解析で一網打尽

保存時にフォーマッタ(Prettier互換)を通すと、未閉鎖箇所で整形が破綻して場所が特定しやすい。

ESLintの「no-unexpected-multiline」「quotes」「template-curly-spacing」を有効化して事故を抑制。

GASでもV8ランタイムであれば一般的なJS整形・Lintの恩恵を受けやすい。

最小再現テンプレ(切り出して特定)

/**
 * 1) エラー行の前後だけを貼る
 * 2) 依存をダミー化(固定値、スタブ関数)
 * 3) 通る所まで削り、崩れる最小差分を見つける
 */
function minimal() {
  const cfg = {
    sheetId: 'xxx',
    range: 'A1'
  }; // ← ここから問題行を追加し、閉じ記号の漏れを確認
}

NG→OK 早見(よくある3パターン)

// 1) 配列やオブジェクトのカンマ
const a = { x: 1 y: 2 };        // NG
const a2 = { x: 1, y: 2 };      // OK

// 2) 関数の閉じ忘れ
function f(x) {
  return x * 2;
// } が足りない

// 3) バッククォートの閉じ忘れ
const s = `user=${Session.getEffectiveUser().getEmail()};
// ` が足りない

再発防止チェックリスト

括弧 (), {}, [] の対応をエディタのペア表示で確認。

‘ ” ` の数が偶数かを検索で点検。

/* … */ を書いたら */ を即時入力するスニペットを導入。

を文字列で扱うときは “<\/script>” にする。

貼り付けコードの全角記号・スマートクォートをASCIIに正規化。

フォーマッタとESLintを保存時に自動実行。

エラー行の直前〜直後20行を最小再現に切り出して調査する。