GAS『Cannot Call Method on Null』の原因と対処法

GAS『Cannot Call Method on Null』の原因と対処法

Google Apps Scriptで「Cannot call method ‘xxx’ of null」や「Cannot read properties of null (reading ‘xxx’)」は、nullに対してメソッド呼び出しを行ったときに出る。どの値がnullなのかを特定し、取得処理・権限・データの前提を直すのが近道。

エラーの意味と代表メッセージ

TypeError: Cannot call method 'getRange' of null
TypeError: Cannot read properties of null (reading 'appendRow')
TypeError: Cannot call method 'getBody' of null

・対象がnullのまま .getRange() や .appendRow() などを呼んでいる。

発生条件の早見表

□ getSheetByName が null を返したのに .getRange() を呼んだ
□ openById / DriveApp.getFileById が対象を見つけられない(ID違い/権限なし/ごみ箱)
□ onEdit/onOpen/doGet/doPost の e が未定義/null(手動実行など)
□ PropertiesService/CacheService から null を受け取り .trim() などを呼んだ
□ 外部APIのJSONで想定キーが null/欠落
□ 正規表現/配列/オブジェクトの操作対象が実は null

まず特定:どの変数がnullかをログで洗い出す

function trace(v, name) {
  console.log(name, {isNull: v === null, isUndef: v === undefined, type: typeof v, value: v});
}

function demo() {
  const ss = SpreadsheetApp.openById('YOUR_SHEET_ID');
  const sh = ss.getSheetByName('Data');
  trace(sh, 'sheet');      // ← nullならここで判明
  sh.getRange('A1').setValue('ok');
}

・例外直前の値を必ず記録してから修正に着手。

最頻出1:getSheetByName が null(シート名違い・末尾空白・誤プロジェクト)

// NG
const sh = SpreadsheetApp.openById(ID).getSheetByName('Data '); // 末尾空白
sh.getRange('A1').setValue('x'); // Cannot call method 'getRange' of null

// OK:安全取得
function getSheetSafe(id, name) {
  const ss = SpreadsheetApp.openById(id);
  const sh = ss.getSheetByName(name);
  if (!sh) throw new Error(`シートが見つかりません: "${name}"`);
  return sh;
}
const sh2 = getSheetSafe(ID, 'Data');

・openByIdにURL全体を渡すミスにも注意(IDのみ渡す)。

最頻出2:ファイル/ID/権限の問題(Drive/Docs/Slides)

// NG:別アカウント所有/ごみ箱/共有ドライブ権限なし
const file = DriveApp.getFileById(FILE_ID); // ここで例外 or null相当の扱いに
const doc = DocumentApp.openById(DOC_ID);   // docがnull → getBodyで落ちる

// OK:存在と状態を確認(権限が無ければ先に共有)
function assertFile(id) {
  const f = DriveApp.getFileById(id);
  console.log({name: f.getName(), trashed: f.isTrashed()});
  return f;
}

・共同編集権がない/ごみ箱入り/共有ドライブのアクセス設定で失敗しやすい。

最頻出3:イベント引数 e が無い(手動実行)

// NG:手動実行で e が undefined → e.range 参照で落ちる
function onEdit(e) {
  const a1 = e.range.getA1Notation(); // TypeError
}

// OK:ガード
function onEdit(e) {
  if (!e || !e.range) return; // 直接実行時は何もしない
  const a1 = e.range.getA1Notation();
}

・doPost/doGetも同様に e?.postData?.contents を必ず確認。

最頻出4:Properties/Cache が null(文字列メソッド呼び出し)

// NG
const token = PropertiesService.getScriptProperties().getProperty('API_TOKEN');
const trimmed = token.trim(); // tokenがnull → TypeError

// OK
const token2 = (PropertiesService.getScriptProperties().getProperty('API_TOKEN') || '').trim();
if (!token2) throw new Error('API_TOKEN が未設定');

・getPropertyは未設定ならnullを返す。デフォルト値か検証を挟む。

最頻出5:外部APIのJSONが期待通りでない(null安全にアクセス)

// NG
const res = UrlFetchApp.fetch(url);
const json = JSON.parse(res.getContentText());
const name = json.data.user.name.toUpperCase(); // dataやuserがnullで落ちる

// OK(オプショナルチェーン/デフォルト)
const name2 = json?.data?.user?.name;
const upper = (typeof name2 === 'string') ? name2.toUpperCase() : 'UNKNOWN';

・失敗時レスポンスや仕様変更でキーが消えることがある。

最頻出6:範囲/値の取り扱い(空とnullの違い)

// getValues() の空セルは ''(空文字)で返るのが通常
const values = sh.getRange(2,1,10,2).getValues();
values.forEach(r => {
  const v = r[0];
  // v === '' なら空セル、v === null のパターンは別API由来で稀
});

・「空」=”と「null」を混同しない。nullチェックは厳密に。

ユーティリティ:nullを早期検知して意味のあるエラーに置換

const U = {
  req(v, msg='必須値がnull/undefinedです') { if (v == null) throw new Error(msg); return v; },
  getSheet(id, name) {
    const ss = SpreadsheetApp.openById(id);
    const sh = ss.getSheetByName(name);
    return this.req(sh, `シートが見つかりません: "${name}"`);
  },
  str(v, def='') { return v == null ? def : String(v); }
};

// 使用例
const sh = U.getSheet(ID, 'Data');
const token = U.str(PropertiesService.getScriptProperties().getProperty('TOKEN'));

・「どこでnullだったか」を明確にし、原因追跡を容易にする。

NG→OKスニペット集

// NG:URLをopenByIdに渡す
SpreadsheetApp.openById('https://docs.google.com/spreadsheets/...');

// OK:IDのみ
SpreadsheetApp.openById('1AbCdEfGhIjK...');

// NG:存在未確認でメソッド呼び出し
const sh = ss.getSheetByName('Data');
sh.appendRow(['a']);  // 失敗時にnullで落ちる

// OK:確認してから
const sh2 = ss.getSheetByName('Data');
if (!sh2) throw new Error('Data シートなし');
sh2.appendRow(['a']);

// NG:e未確認
function doPost(e){ const body = e.postData.contents; /* ... */ }

// OK
function doPost(e){
  const body = e?.postData?.contents;
  if (!body) return ContentService.createTextOutput('empty');
  // ...
}

再現→修正の通し例(シート名ミス)

// 再現(落ちる)
function failCase() {
  const ss = SpreadsheetApp.openById('YOUR_SHEET_ID');
  const sh = ss.getSheetByName('Data ');   // スペース付き
  sh.getRange('A1').setValue('x');         // Cannot call method 'getRange' of null
}

// 修正(安全取得)
function fixed() {
  const ss = SpreadsheetApp.openById('YOUR_SHEET_ID');
  const name = 'Data';
  const sh = ss.getSheetByName(name);
  if (!sh) throw new Error(`シート "${name}" が見つかりません`);
  sh.getRange('A1').setValue('ok');
}

チェックリスト(上から順に)

□ 例外直前の変数をログして、nullの正体を特定したか
□ シート名/ID/URLの渡し方は正しいか(IDのみ・末尾空白なし)
□ 対象ファイルに編集権限があり、ごみ箱/共有ドライブ設定も問題ないか
□ イベント引数 e の存在を確認し、手動実行時は安全に抜けるか
□ Properties/Cache のnullをデフォルト値や検証で吸収しているか
□ 外部JSONには ?. と型確認でアクセスしているか
□ 「空文字」と「null」を区別して扱っているか