Google Apps ScriptでTypeError: Cannot read properties of undefined (reading 'getValue')を修正する

beginner📗 Google Sheets2026-05-14| Google Apps Script、Google Sheets、V8ランタイム

Error Message

TypeError: Cannot read properties of undefined (reading 'getValue')
#apps-script#javascript#getvalues#undefined#反復処理

エラーの内容

TypeError: Cannot read properties of undefined (reading 'getValue')

.getValue().getValues().getRange()などのメソッドを、実行時にundefinedとなっている変数に対して呼び出したことが原因です。よくある3つのトリガー:誤ったインデックスで行をループしている、存在しないシートを参照している、またはgetActiveSheet()が常に有効な値を返すと思い込んでいる。

根本原因

Apps ScriptはV8 JavaScriptで動作します。undefinedに対してメソッドを呼び出すと、例外なくこのエラーが発生します。主な原因はこちらです:

  • 配列インデックスの範囲外アクセスrows[i]でループしているが、配列の要素数が想定より少ない場合、10行の配列に対してrows[10]undefinedになります。
  • シート名の誤りspreadsheet.getSheetByName('Data')はシートが存在しない場合にnullを返します。そのnullに対して.getRange()を呼び出すと同様のエラーが発生します。
  • 空のレンジの誤った処理:空のレンジに対してgetValues()を呼び出すと、undefinedではなく空文字の2次元配列が返されます。ただし、次元を読み間違えるとundefinedのセルを参照してしまう可能性があります。
  • 行インデックスのオフバイワンgetRange(row, col)などのシートメソッドは1始まりです。getValues()から得られる配列は0始まりです。混同すると配列の末尾を1つ超えた行を読み取ってしまいます。

修正1:シートの参照を使用前に確認する

名前でシートを取得する場合、存在するとは限りません。スプレッドシートのタブ名を変更するだけでスクリプトが静かに壊れます。2行のガード処理でそれを防げます:

function readData() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const sheet = ss.getSheetByName('Data');

  if (!sheet) {
    Logger.log('Sheet "Data" not found');
    return;
  }

  const value = sheet.getRange(1, 1).getValue();
  Logger.log(value);
}

注意:getSheetByName()undefinedではなくnullを返します。ただし関係ありません — null.getValue()も同じエラーをスローします。if (!sheet)チェックで両方をキャッチできます。

修正2:getValues()を使ったループのオフバイワンを修正する

これは典型的な落とし穴です。getValues()は0始まりの配列を返しますが、以下のループはi = 1から開始しているため、最後のイテレーションでdata[lastRow]が配列の末尾を超えてしまいます:

// BROKEN: starts loop at 1, but array index goes 0 to lastRow-1
function brokenLoop() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const lastRow = sheet.getLastRow();
  const data = sheet.getRange(1, 1, lastRow, 3).getValues();

  for (let i = 1; i  {
    row.forEach((cell, colIndex) => {
      Logger.log(`[${rowIndex}][${colIndex}]: ${cell}`);
    });
  });
}

修正4:チェーン呼び出しの中間結果を確認する

findNext()は何も見つからない場合にnullを返します。そのまま.getValue()をチェーンすると、キーワードが存在しないたびにクラッシュします:

// BROKEN: assumes the keyword always exists in the sheet
function findAndRead() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const found = sheet.createTextFinder('keyword').findNext();
  const value = found.getValue(); // null.getValue() → TypeError
}

// FIXED
function findAndReadSafe() {
  const sheet = SpreadsheetApp.getActiveSheet();
  const found = sheet.createTextFinder('keyword').findNext();

  if (!found) {
    Logger.log('Keyword not found in sheet');
    return;
  }

  const value = found.getValue();
  Logger.log(value);
}

修正5:存在しない名前付き範囲へのアクセス

getRangeByName()は、スプレッドシートで定義されていない範囲名に対して静かにnullを返します。読み取り前に必ず確認してください:

// BROKEN
function readNamedRange() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const range = ss.getRangeByName('MyConfig'); // null if not defined
  const value = range.getValue();              // TypeError
}

// FIXED
function readNamedRangeSafe() {
  const ss = SpreadsheetApp.getActiveSpreadsheet();
  const range = ss.getRangeByName('MyConfig');

  if (!range) {
    Logger.log('Named range "MyConfig" does not exist');
    return;
  }

  const value = range.getValue();
  Logger.log(value);
}

デバッグのヒント:クラッシュ前に変数をログ出力する

Apps Scriptのスタックトレースは曖昧なことがあります。どの変数がundefinedか不明な場合は、失敗している呼び出しの直前にログ行を追加してください:

Logger.log('sheet: ' + sheet);
Logger.log('range: ' + range);
Logger.log('data type: ' + typeof data);

表示 → ログを開くか(またはスクリプトエディタでCtrl+Enterを押す)と、クラッシュ前にどの変数がnullまたはundefinedを出力したかが正確にわかります。スタックトレースを読むより素早く原因を特定できます。

確認方法

修正を適用したら、実際に問題が解決されていることを確認してください:

  • Apps Scriptエディタで実行 → 関数を実行から手動で関数を実行する。
  • 表示 → 実行数を開く — 正常な実行では赤いエントリが表示されません。
  • 意図的にエッジケースをテストする:空のシート、名前が変更されたタブ、データのないレンジ。これらがバグを露呈した正確な条件です。
  • 時間ベースのトリガーを使用している場合は、次回のスケジュール実行後に表示 → 実行数を確認して、サイレント障害がないことを確認してください。

予防策

  • getSheetByName()getRangeByName()findNext()はnullを返す可能性があるものとして扱う。出力を使用する前に必ずif (!result) return;を追加してください。
  • シートの生の行数ではなくdata.lengthでループを制御する。配列はすでに自分の行数を知っています。
  • インデックスの考え方を正確に把握する:シートの行と列は1始まり、配列のインデックスは0始まり。一方が必要な場所でもう一方を使わないようにする。
  • シートデータを読み取る関数の先頭にif (lastRow < 1) return;を追加する。1行追加するだけで、エラーのクラスをまるごと防ぐことができます。

Related Error Notes