TL;DR: 即効の解決策
範囲の座標をハードコーディングするのはやめましょう。このエラーのほとんどは、データ配列の行数と、対象となるスプレッドシートの範囲が一致していないことが原因です。解決するには、データのサイズに合わせて範囲を自動的に定義するようにします。
// 避けるべき方法:データが増えるとエラーになります
// sheet.getRange("A1:B10").setValues(data);
// 推奨される方法:データのサイズに適応します
if (data && data.length > 0) {
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);
}
根本原因
Google の setValues() メソッドは非常に厳密です。2次元配列と出力先の範囲が、完璧な長方形として一致することを想定しています。配列に500行のデータがあるのに、getRange() で指定した範囲が499行しかない場合、スクリプトは即座に停止します。
よくある失敗の原因は以下の通りです:
- 静的な範囲指定:
"A2:C100"のように範囲を固定しているが、データソースが101行に増えた。 - 次元の不一致:
[[1], [2], [3]]のような必要なネスト形式ではなく、[1, 2, 3]のような単純なリストを渡している。 - 空のフィルタ結果: データセットをフィルタリングしたが、結果のセットがクリアした元の範囲よりも小さくなっている。
信頼性の高い解決策
1. 範囲サイズの動的指定
最終行や最終列を手動で計算するとバグの原因になります。代わりに、配列の length プロパティを使用してください。これにより、データに必要なスペースを正確にスプレッドシート上に確保できます。
function updateSheetSafely(data) {
const ss = SpreadsheetApp.getActiveSpreadsheet();
const sheet = ss.getSheetByName("レポート");
// データセットが空の場合のエラーを防ぐガード句
if (!data || data.length === 0) return;
const startRow = 1;
const startCol = 1;
const numRows = data.length;
const numCols = data[0].length;
// この範囲はデータのサイズに合わせて常に拡大・縮小します
sheet.getRange(startRow, startCol, numRows, numCols).setValues(data);
}
2. 1次元配列から2次元配列へのマッピング
Apps Script では、["値 1", "値 2"] は単なるフラットなリストとして扱われ、グリッドとは見なされません。setValues() はこの形式を受け付けないため、データに「高さ」や「幅」を持たせるようにラップする必要があります。
// 1つの「行」として書き込む場合 (1行3列)
const rowData = [["値 1", "値 2", "値 3"]];
sheet.getRange(1, 1, 1, rowData[0].length).setValues(rowData);
// 1つの「列」として書き込む場合 (3行1列)
const flatArray = ["A", "B", "C"];
const colData = flatArray.map(item => [item]); // 結果: [["A"], ["B"], ["C"]]
sheet.getRange(1, 1, colData.length, 1).setValues(colData);
3. オフバイワン・エラー(1の誤差)のデバッグ
ループ内で配列を作成していると、最後に誤って空の行を追加してしまうことがよくあります。スクリプトがクラッシュする直前に console.log() を使用して次元を確認してください。これにより、スクリプトが実際に認識しているサイズが判明します。
console.log("配列の行数: " + data.length);
console.log("配列の列数: " + data[0].length);
// これらの数値が範囲と一致しない場合、スクリプトは失敗します。
修正の確認方法
スクリプトが本番環境で動作するか、以下の3点を確認してください:
- 実行ログ: 緑色の「完了」チェックマークを確認します。「失敗」と表示される場合は、依然として次元が一致していません。
- シートの境界: データの最終行を確認します。動的な範囲指定により、隙間を残したり誤ったセルを上書きしたりすることなく、データが完璧に収まっているはずです。
- 負荷テスト: 1行のデータで実行し、次に1,000行で試してください。堅牢なスクリプトは、コードを変更することなく両方のケースを処理できます。
プロのヒント:「ゴーストデータ」を避ける
動的な範囲指定でクラッシュは防げますが、古いデータが残ってしまう可能性があります。新しいデータが10行で、古いデータが50行あった場合、setValues() は最初の10行しか上書きしません。レポートを綺麗に保つために、常に書き込み先の範囲を先にクリアしてください。
// 新しいデータを書き込む前にすべてクリアする
sheet.getRange(1, 1, sheet.getLastRow(), sheet.getLastColumn()).clearContent();
sheet.getRange(1, 1, data.length, data[0].length).setValues(data);

