You call a function. Nothing happens. You open the console — "TypeError: undefined is not a function." You stare at the code. The function is right there. You defined it yourself. You've called it a hundred times before. And yet — undefined is not a function. This error has wasted more developer hours than almost any other, and the fix is almost always hiding in plain sight.
What This Error Actually Means
When JavaScript throws this at you, it means your code tried to run something as a function — but when the browser actually looked at that spot, it found nothing. JavaScript fully expected a usable chunk of code it could run, found a blank space instead, and crashed.
Depending on your browser or setup, you'll usually see one of these three forms:
TypeError: undefined is not a functionTypeError: X is not a function(where X is your variable name)TypeError: Cannot read properties of undefined (reading 'call')
All three mean the same thing — you're trying to call something that isn't a function. The real question is: why did that variable become undefined in the first place?
Key thing to remember: The line number in the console is always the exact spot where the crash happened. Start your investigation there — not anywhere else in the file.
The #1 Cause — Typo in the Function Name
JavaScript is case-sensitive. To the browser, myfunction and myFunction are completely different names — they might as well be from two different planets. If you define calculateTotal but call calculatetotal, JavaScript searches for the lowercase version, finds nothing, hands you undefined, and then tries to run that nothingness as a function. Crash.
- This mistake is the hardest to spot because your brain autocorrects what your eyes read — you'll look at it ten times and see nothing wrong
- Open DevTools Console, type the exact function name, and hit Enter — if it returns
undefined, your spelling doesn't match your definition - Use VS Code autocomplete — let the editor type function names for you, never retype them manually
- Copy-paste function names instead of retyping them whenever possible
The #2 Cause — Calling a Method That Doesn't Exist on That Type
Every data type in JavaScript has its own set of built-in tools (called methods). When you call a method that doesn't exist on a specific type, JavaScript looks it up, finds nothing, and tries to call that nothing as a function — same crash, different cause.
- Calling
.toUpperCase()on a number — numbers don't have that method - Calling
.map()onundefined—undefinedisn't an array, it has no methods at all - Calling
.trim()onnull— null has nothing - The fix — drop a
console.logright above the crash line and check what type the variable actually is at that moment
// ❌ Unsafe — crashes if myVar is not a string
myVar.toUpperCase();
// ✅ Safe — check the type first
if (typeof myVar === 'string') {
myVar.toUpperCase();
}
The #3 Cause — Accidentally Overwriting a Built-in
This is a sneaky mistake that even experienced developers make. The moment you name your variable after a built-in JavaScript feature, you quietly wipe out the original tool and replace it with your own data.
const map = "some string"— the built-in array.map()is now gone in that scope, replaced by a stringconst filter = []— the built-in.filter()no longer worksconst fetch = null— you just broke every single API call on your page- The fix — if you're unsure about a name, type it in DevTools console first. If it already returns something, pick a different name
The #4 Cause — Wrong this Context in Callbacks
This one hits developers who are past the beginner stage and start working with objects and callbacks. When you pass a regular function inside another function as a callback, the meaning of this changes behind the scenes. If your function depends on this.someMethod(), and this is no longer pointing to your object — this.someMethod becomes undefined and calling it crashes everything.
- Use arrow functions
() => {}for callbacks — arrow functions don't create their ownthis, they inherit it from the surrounding code - If you must use a regular function, lock down the context manually with
.bind(this)
// ❌ Regular function — 'this' changes context
setTimeout(function() {
this.doSomething(); // 'this' is now window, not your object
}, 1000);
// ✅ Arrow function — 'this' stays the same
setTimeout(() => {
this.doSomething(); // 'this' is still your object
}, 1000);
The Safe Debugging Checklist
The next time you see this error, don't panic or start randomly changing your code. Run through these five steps in order — they will find the cause every single time:
- Check the exact name — is your function spelled and capitalized identically to how it was defined?
- Log the variable — throw a
console.log()right above the crash line and see exactly what that variable contains at that moment - Check the type — use
typeofto verify if it's actually a function, or if it accidentally became a string, number, or undefined - Look for overwrites — scan your code for any place where you might have reused a built-in JavaScript name as a variable
- Verify the context — if this is happening inside a callback, check whether
thisis still pointing to what you expect
Open any project where you've seen this error. Find the exact line the console pointed to. Before changing a single thing — console.log whatever you're trying to call and read what it actually is. Not what you named it. Not what you think it should be. What it actually is at that exact moment. Do that once, and you will never spend more than two minutes on this error again.