2026年10月18日補足:160KBという目安の適用範囲
本稿の約160KBは、当時わたしの環境で観察した目安です。HtmlService全体に共通する上限や、160KBを超えると必ず切れるという仕様を示すものではありません。今回確認したGoogle公式のサービスの割当・制限とHTML Serviceの制限には、この値の記載を確認できませんでした。正確な境界や原因の一般化、現在の同じ環境での再現は未検証です。120KBは、わたしが自分の検査に置いた基準です。
当時わたしのGASアプリでは、約170KBの1つのHTMLファイルを使う画面で、ブラウザに届いたスクリプトが途中で終わる現象を観察しました。約160KBは当時の観察から置いた目安で、正確な境界やHtmlService共通の上限を確認したものではありません。わたしは画面のファイルを2つに分け、120KBを超えたらデプロイ前に止める検査を足して対処しました。
わたしはノートとマインドマップの画面を持つGASのWebアプリを、自分で作って使っています。画面はHTMLファイルを画面ごとに分け、include() で1枚のページに組み立てる作りです。今回はその中のマインドマップの画面だけが、ある日から本番で開かなくなった記録です。
起きたこと: 本番だけ構文エラー
症状はこうでした。
- マインドマップの画面だけが動かない
- 本番のページで起きたエラーを記録しているシートには、JavaScriptの構文エラーが残っていた
- ほかの画面(ノート一覧・エディタなど)は普通に動く
構文エラーなら、書き間違いを探せば済むはずです。ところが、GASのエディタで開いたファイルは最後まで完全でした。手元に取ってきたファイルをNode.jsで構文チェックしても、エラーは出ません。
保存されている中身は正しいのに、ブラウザに届いた中身だけが壊れている。この時点で、疑う場所が「わたしのコード」から「GASが配信する経路」に変わりました。
原因: ファイルが大きすぎた
壊れていたマインドマップの画面は、JavaScriptをすべて1つのHTMLファイルに入れていました。記録では、壊れたときのファイルは約170KBです。ほかの画面のファイルは大きいものでも60KB前後でした。
ブラウザに届いた中身を見ると、スクリプトが途中で終わっていました。閉じるべき括弧が来る前にファイルが切れているので、構文エラーになります。保存は完全、届く中身だけが途中まで。わたしの環境では、およそ160KBを超えたあたりから、この切れ方が起きていました。
注意しておくと、「160KB」はわたしが観察した目安で、Googleが公開している数字ではありません。何バイトちょうどで切れるのかを細かく測ったわけでもありません(境目は未計測)。
ローカルの検証で再現しなかった理由も、これで説明がつきます。手元のファイルは完全なので、手元で何度チェックしても通ります。壊れるのは配信の段階だけです。
対処1: 1つのスクリプトを2ファイルに分けた
ファイルを小さくすればいいので、分けました。ただ、マインドマップの処理は1つの大きな関数(即時実行関数)の中に全部入っていて、途中で <script> を閉じて次の <script> を始める、という分け方はできません。括弧の途中で切ることになるからです。
そこで、次の形にしました。
1. スクリプトの本体を、実行しない形(type="text/plain")で2つのファイルに半分ずつ置く
2. 2つめのファイルの最後に、2つをつなげて実行する短いスクリプトを置く
3. つなげる前に部品が欠けていたら、エラーとして記録する
text/plain にしたスクリプトは、ブラウザが実行しません。ただの文字列として置かれるだけなので、どこで切っても構文エラーになりません。
サーバー側は、よくある include() のままです。
// コード.gs
function doGet() {
return HtmlService.createTemplateFromFile('index').evaluate();
}
function include(name) {
return HtmlService.createHtmlOutputFromFile(name).getContent();
}
ページ側で、2つのファイルを続けて読み込みます。
<!-- index.html -->
<?!= include('PartA'); ?>
<?!= include('PartB'); ?>
1つめのファイルには、本体の前半だけを置きます。
<!-- PartA.html -->
<script type="text/plain" id="srcA">
var BigModule = (function () {
// ……前半……
</script>
2つめのファイルに、後半と、つなげて実行する部分を置きます。
<!-- PartB.html -->
<script type="text/plain" id="srcB">
// ……後半……
return { start: start };
})();
</script>
<script>
(function () {
try {
var a = document.getElementById('srcA');
var b = document.getElementById('srcB');
if (!a || !b) throw new Error('分割したファイルの片方が届いていません');
new Function(a.textContent + '\n' + b.textContent + '\nwindow.BigModule = BigModule;')();
} catch (e) {
console.error('[BigModule]', e);
}
})();
</script>
分けたあとの2ファイルを、いま wc -c で測るとこうなっていました。
| ファイル | サイズ |
|---|---|
| 前半 | 82,718 バイト(約81KB) |
| 後半 | 89,252 バイト(約87KB) |
| 合計 | 171,970 バイト(約168KB) |
分けてからも少し機能を足しているので、合計は壊れたときの約170KBとほぼ同じです。それでも1ファイルあたりは90KB未満なので、配信で切れることはなくなりました。
対処2: 120KBを超えたらデプロイ前に止める
分けて直っても、また育てば同じことが起きます。しかも、起きるのは本番だけです。そこで、デプロイ前に必ず走らせている構文チェックに、ファイルサイズの検査を足しました。
上限は、切れはじめた約160KBではなく、余裕を見て120KBにしています。境目を細かく測っていないので、手前で止めるほうが安全だと考えました。
同じ考え方の最小例です。claspで取ってきたフォルダに置いて、node check-size.js で動かします。
// check-size.js — 120KBを超える .html / .js があれば止める
const fs = require('fs');
const path = require('path');
const LIMIT = 120 * 1024;
let ng = 0;
for (const name of fs.readdirSync(__dirname)) {
if (!/\.(html|js)$/.test(name)) continue;
const bytes = fs.statSync(path.join(__dirname, name)).size;
if (bytes > LIMIT) {
ng++;
console.error(`NG ${name}: ${Math.round(bytes / 1024)}KB(上限 ${LIMIT / 1024}KB)。分割してください`);
}
}
if (ng) process.exit(1);
console.log('サイズ OK');
clasp push の前にこれを通す、と決めておけば、大きくなりすぎたファイルが本番に出る前に気づけます。
対処3: 本番のエラーをシートに残す
今回いちばん時間を使ったのは、「本番で何が起きているか」を知るところでした。本番だけで起きる不具合は、手元では見えません。
そこで、ブラウザで起きたエラーを、GAS経由でスプレッドシートに1行ずつ書く仕組みを入れています。これも最小例です。
<script>
window.addEventListener('error', function (ev) {
google.script.run.logClientError({
message: String(ev.message || ''),
source: String(ev.filename || ''),
line: ev.lineno || 0,
page: location.href
});
});
</script>
// コード.gs
function logClientError(err) {
const sheet = SpreadsheetApp.openById('スプレッドシートのID').getSheetByName('エラー記録');
sheet.appendRow([new Date(), err.message, err.source, err.line, err.page]);
}
これがあると、「本番で開けない」と気づいたときに、まずシートを見れば、どの画面で何のエラーが出たかがわかります。書き込む内容は最小限にして、利用者の入力内容などは送らないようにしています。
つまずいた点
- 手元のチェックが通ることを、正しさの証拠だと思っていた。 今回は手元のファイルが正しかったからこそ、チェックが通っていました。検査が見ているのは「保存された中身」で、「届いた中身」ではありませんでした。
- 構文エラーという表示に引っぱられた。 最初は書き間違いを探していました。「保存は完全なのに本番だけ」という食い違いに気づくまでが長かったです。
- サイズの境目は測っていない。 約160KBはわたしの環境で見た目安です。ファイルの中身(日本語が多いかどうかなど)で変わるのかも、未計測です。
わたしが学んだこと
GASのHTMLファイルは、画面ごとに分けていても、1つの画面が育つと知らないうちに大きくなります。そして大きくなりすぎたときの壊れ方が、「本番だけ」「保存は完全」という、いちばん探しにくい形でした。
いまは、画面のファイルが大きくなってきたら、最初から分けるようにしています。120KBの検査はその合図です。


コメント