Google Apps Scriptで実際にハマった5つの落とし穴|日本語スプレッドシート特有の罠
5人が使う業務スプレッドシートを自動化しています。動かない原因の大半は文法ミスではなく、日本語環境特有の事情と実行順序でした。実際に踏んだ落とし穴を、原因と直し方つきで書きます。
前提:何を自動化しているか
マネージャー5人がそれぞれ別のスプレッドシートに入力し、その内容を本体の管理表と双方向で同期する仕組みを運用しています。
- 本体の担当タブから、各マネージャーのファイルへ名簿を配る
- 各ファイルの入力を、本体のダッシュボードに集約する
- 対象月のプルダウンを変えると、表示データが切り替わる
タブ単位の閲覧制限ができないため、ファイル自体を分ける必要があったという事情から生まれた構成です。
動かなくなった原因を並べると、文法ミスはほぼありませんでした。 詰まったのは全部、下に書く種類のものです。
落とし穴1:全角数字は数値判定を全部すり抜ける
これが一番時間を溶かしました。
管理番号の列を条件にデータを絞り込もうとしたら、1件も取れません。値は入っているのにです。
原因は、セルの中身が全角数字の文字列だったことでした。しかも一部には半角スペースが混ざっていました。
"1" ← 全角の1
"1 0" ← 全角の10、間にスペース
人が入力する列では普通に起こります。日本語入力のまま数字を打てば全角になります。
こう書いても弾かれます。
// どちらも通らない
.filter(row => typeof row[0] === 'number')
.filter(row => /^[0-9]+$/.test(row[0]))
正しくは、空白を除去してから半角・全角の両方を許可します。
.filter(function(row) {
var s = String(row[0] || '').replace(/[\s ]/g, '');
return s.length > 0 && /^[0-90-9]+$/.test(s);
});
ポイントは2つです。
[\s ]に全角スペースも入れる(\sは全角スペースにマッチしません)- 正規表現に
0-9を含める
日本語環境のスプレッドシートを扱うなら、数値判定は最初からこの形にしておくのが安全です。
落とし穴2:データ検証の更新より先に値を書くと、以降が全部止まる
対象月を選ぶプルダウンがあります。新しい月のシートが増えるたびに、選択肢が増える設計です。
あるとき、5人のうち3人までは同期されるのに、4人目以降が更新されなくなりました。
原因は処理の順序でした。
// 誤り:この順序だと、新しい月がまだ選択肢に無い
sheet.getRange('O1').setValue(targetMonth); // ← 弾かれて例外
sheet.getRange('O1').setDataValidation(newRule); // ← ここまで届かない
プルダウンには「リスト内の値のみ許可」という検証ルールがついています。新しい月をセットしようとしても、選択肢が古いままなので例外になります。
しかも処理を forEach で回していたため、例外が出た時点で残りの人が全員処理されませんでした。
正しい順序はこうです。
// 正しい:先に選択肢を更新してから値を書く
sheet.getRange('O1').setDataValidation(newRule);
sheet.getRange('O1').setValue(targetMonth);
検証ルールがある場所に書き込むときは、ルールの更新を必ず先に行う。当たり前に見えますが、シートが増える設計だと毎回この順序が問われます。
教訓:forEach の中で例外を出さない
もう1つの学びは、繰り返し処理の中で例外を握りつぶすかどうかでした。
1人ぶんの処理が失敗しても、残りは処理してほしい場面があります。その場合は個別に try で囲み、失敗したことを記録して続行する形にします。
全員止まるか、1人だけ失敗するか。 設計時に決めておくべきでした。
落とし穴3:静的値と数式が混在すると、更新漏れが起きる
シート間のデータ受け渡しには、2つの方式が混在していました。
| 方式 | 更新のタイミング |
|---|---|
| 数式(VLOOKUP、FILTER) | 即座 |
| スクリプトが書いた静的値 | 実行するまで古いまま |
担当者の割り当てを変更したとき、ダッシュボードの数字はすぐ変わりました。 そちらは数式で参照していたからです。
しかし実際にマネージャーのファイルへ配られる名簿は、古いままでした。 途中に静的値のシートを挟んでいたためです。
画面の一部だけが即座に更新されると、全体が更新されたと錯覚します。 これが厄介でした。
対策として、更新の手順を明文化しました。
1. 静的値のシートを更新するスクリプトを実行
2. そのあとで配布のスクリプトを実行
どこが数式で、どこが静的値なのかを把握していないと、この順序は出てきません。
落とし穴4:書いた直後にクリアする処理を呼んでしまう
一括セットアップの処理で、書き込んだデータが消えるという現象が起きました。
原因は、セットアップ処理の中で「表の下部をクリアする関数」を呼んでいたことでした。データを書き込む処理の直後に、それをクリアする処理が走っていたわけです。
データを書く → 下部をクリアする ← 書いたものが消える
初期化のつもりで置いた処理が、順序の変更で意味が変わっていました。
一括処理の中で「消す」「初期化する」系の関数を呼ぶときは、それが何を消すのかを毎回確認する必要があります。当方では呼び出し自体を削除し、再追加してはいけない旨をメモに残しました。
落とし穴5:自動トリガーは無限ループしやすい
シートの追加を検知して自動更新する仕組みを入れたところ、更新処理が自分自身を再度呼ぶ危険がありました。
対策は2つ入れています。
// 1. 変化があったときだけ処理する(キャッシュと比較)
var cached = PropertiesService.getScriptProperties().getProperty('CACHE_KEY');
if (current === cached) return; // 変化なし → 何もしない
// 2. キャッシュを先に更新してから本処理
PropertiesService.getScriptProperties().setProperty('CACHE_KEY', current);
キャッシュの更新を本処理より先にやるのがポイントです。あとにすると、処理中に再度トリガーが発火したときに条件をすり抜けます。
あわせて LockService で同時実行も防いでいます。
つまずきやすい点
実行完了の表示まで待つ
一括処理を実行すると、ログにURLなどが先に出ます。そこで終わったと思って画面を閉じると、裏でまだ処理が続いています。
実際、完了まで20〜40秒かかる処理があります。「実行完了」の表示が出るまで待つ必要がありました。
関数の選択が不安定なとき
エディタの関数ドロップダウンは、スクロールすると閉じてしまうことがあります。
確実に実行したい処理は、よく使う関数の末尾から呼ぶ形にしておくと安全です。当方では一括セットアップの最後に、必要な処理をまとめて呼ぶ形にしました。
シート名の表記ゆれ
2026-06-30 と 2026-6-30 のように、桁数が揃わないシート名が混ざることがあります。人が手で作るためです。
正規表現で \d{4}-\d{2}-\d{2} と決め打ちしていると、1桁表記のシートが見つかりません。正規化してから照合する関数を挟むと解決します。
よくある質問
Apps Scriptでセルの数値が取得できないのはなぜですか?
全角数字で入力されている可能性があります。全角数字は文字列として扱われるため、typeof による数値判定にも、半角数字のみの正規表現にも一致しません。空白(全角スペースを含む)を除去したうえで、半角と全角の両方を許可する正規表現で判定してください。
setValueがエラーになるのはなぜですか?
そのセルにデータ検証ルールが設定されており、書き込もうとした値が選択肢に含まれていない可能性があります。選択肢が動的に増える設計の場合は、値を書き込む前に検証ルールを更新してください。
onChangeトリガーが無限ループしないようにするには?
処理の前に「前回から変化があったか」を判定し、変化がなければ何もせず終了させます。判定に使う値はScriptPropertiesなどに保存し、本処理より先に更新してください。あとから更新すると、処理中に再発火した際に条件をすり抜けます。
まとめ
Apps Scriptで詰まる原因は、文法ではありませんでした。
- 全角数字は数値判定をすり抜ける(全角スペースにも注意)
- データ検証の更新は、値の書き込みより先
- 静的値と数式の混在は更新漏れを生む
- 消す処理の呼び出し位置で、書いたデータが消える
- 自動トリガーはキャッシュ判定を先に
どれも、日本語環境で人が手入力するスプレッドシートだから起きることです。きれいなデータを前提にコードを書くと、必ずどこかで止まります。