Why getSheetByName returns null instead of throwing
getSheetByName does not throw an exception when it can't find the sheet. It returns null silently. Your script then tries to call getRange (or getLastRow, or anything else) on that null, and that's when the TypeError fires — one step removed from the actual problem, which is the name mismatch.
The match is case-sensitive and whitespace-sensitive. A tab named 'Sales Data ' (trailing space) is a different string from 'Sales Data'. This happens constantly when someone renames a tab by hand, copies a spreadsheet from a template, or pastes a tab name from a doc with a hidden non-breaking space. The error message names getRange as the offending line, so most people look at getRange — but the real fault is two lines earlier.