TypeError: undefined is not a function means you tried to call something that isn't callable — you referenced a variable or property that turned out to be undefined and then tried to invoke it with (). It's one of the two most common runtime errors in JavaScript, right alongside cannot read properties of undefined. The frustrating part is that the message doesn't always tell you which reference was undefined or why, so tracking it down can mean hunting through nested scope, imports, and closures. This guide walks you through diagnosing and fixing it.
What the error means
When you write code like this:
const result = myFunction();
JavaScript expects myFunction to be a function. If it's undefined instead — whether by typo, scope collision, missing import, or async timing — the runtime throws undefined is not a function. It's not a type error in the language sense; it's a reference error that says "I found a variable, but it's not callable."
Modern Node and Chrome phrase this as TypeError: X is not a function where X is the variable name. Older browsers sometimes say TypeError: undefined is not a function generically. Either way, some reference resolved to undefined and you tried to call it.
Five ways you'll hit this error
Nearly every production occurrence comes from one of these patterns:
- Typo in the name.
callbackFn()when you meantcallbackFunction(), orthis.fetchData()when the method is spelled differently. - Missing import. You reference a utility or helper that was never imported, so the name is
undefinedin the current module. - Scope collision. A local variable shadows a function, or you're inside a callback where
thisdoesn't point where you expect. - Async timing. You assign a function conditionally or fetch it dynamically, then try to call it before the assignment.
- Destructuring or object access wrong. You try to call a property of an object that doesn't exist, like
config.handlers.onError()whenhandlersis not an object.
How to fix it
Check for typos first
The simplest cause is the most common. Search your codebase for the function name:
// Wrong — typo in callback name
element.addEventListener('click', () => {
submitForm(); // throws if submitForm is undefined
});
// Right — use the correct name
element.addEventListener('click', () => {
submitForm(); // defined at module scope
});
If the name exists elsewhere but not in your current scope, the next fix applies.
Add the import
If you're calling a function from another module and forgot to import it, you'll get this error. Make sure every external function is imported at the top of your file:
// Wrong — missing import
export function processData(items) {
return items.map(transformItem); // throws if transformItem is not defined
}
// Right — import first
import { transformItem } from "./utils.js";
export function processData(items) {
return items.map(transformItem);
}
Be careful with optional references and defaults
If a function might be optional, guard it before calling:
// Wrong — callback could be undefined
function fetch(url, options) {
const result = getJSON(url);
options.onComplete(result); // throws if onComplete doesn't exist
}
// Right — check before calling
function fetch(url, options) {
const result = getJSON(url);
if (options?.onComplete) {
options.onComplete(result);
}
}
// Or use a no-op default
function fetch(url, options) {
const result = getJSON(url);
const handler = options?.onComplete ?? (() => {});
handler(result);
}
Handle dynamic imports and async loading
If you're loading functions dynamically or conditionally, the reference won't exist until the load completes. Check before calling:
// Wrong — calling before async load
let handler;
fetch('/config').then(res => {
handler = res.data.handler;
});
handler(); // throws immediately; fetch hasn't resolved yet
// Right — call inside the promise chain
let handler;
fetch('/config').then(res => {
handler = res.data.handler;
handler(); // safe; we're inside the then
});
Check object access in callbacks and closures
When you pass a function as a callback, make sure you're not breaking its context. This is especially common in React and event handlers:
// Wrong — loses method context
class Form {
submitForm() {
console.log("Submitting...");
}
render() {
return <button onClick={this.submitForm}>Submit</button>;
}
}
// this.submitForm() is undefined when clicked
// Right — bind or use arrow function
class Form {
submitForm = () => {
console.log("Submitting...");
};
render() {
return <button onClick={this.submitForm}>Submit</button>;
}
}
In modern React, always use arrow functions for class methods or convert to functional components with hooks. Arrow functions capture this lexically, so you'll never lose the reference.
Tracking it down in production
Locally you can paste typeof myFunction in the console and see undefined immediately. In production you have a minified stack trace. The stack frame should tell you the line where the call happens, but you need source maps to map it back to your original code — otherwise you're reading index-4f9a.js:1:52210.
Once you have the real code, add breadcrumbs around async operations or conditional assignments so you can see the path that led to the error. A breadcrumb like "loaded plugins" or "config fetch failed" tells you whether the function was supposed to be loaded when it was called.
The faster way: use error tracking that automatically captures the full stack trace, breadcrumb trail, and affected user. Then you see that myFunction is not a function happened to 23 users on the checkout page after plugin load failed — and the fix becomes obvious.
Don't mask this error with try/catch and a console.log. undefined is not a function is always a bug in your code; catching it and continuing usually means the function never runs and something downstream breaks silently, making the real issue harder to find.
Prevention
The best fix is never hitting the error at all. Use TypeScript to catch undefined references at compile time, write unit tests for callback flows, and validate imports at module boundaries. When you do catch one in production, error tracking with source-mapped stack traces tells you exactly whether it's a typo (fix the name), a missing import (add the import), or a timing issue (add a guard). For the general workflow of hunting production errors when you can't attach a debugger, see how to debug production errors.
Start tracking errors in minutes
Catch undefined is not a function errors with source-mapped stack traces and breadcrumb context — sign up for LightTrace free and see your first issues in minutes.
The error message is terse, but the fix is almost always quick once you know where to look. Whether it's a typo, a missing import, or a scope issue, error tracking turns a mystery into a one-line fix.