Quick answer
You called X(...) but X isn't a function at that point — it's undefined, an object, or something else non-callable. The three usual causes are a default/named import mismatch, a typo or wrong method for the value's real type, or a local variable shadowing the function you meant to call. Log typeof X right before the call to see what you actually have.
The exact error string
const result = data.calculate(5);
// Uncaught TypeError: data.calculate is not a function
import foo from "some-lib";
foo();
// Uncaught TypeError: foo is not a function
How to diagnose it in 30 seconds
Right before the failing call, log console.log(typeof X, X). The type tells you which cause applies: "undefined" — the import/export is mismatched or the property doesn't exist; "object" — you have the whole module or a data object instead of the function itself; a function, but the wrong one — a shadowing variable further down in scope.
Cause 1: default vs named export mismatch
// lib.js
export function formatDate(d) { /* ... */ } // named export, no default
// ❌ app.js
import formatDate from "./lib.js";
formatDate(); // TypeError: formatDate is not a function (imported the whole module)
// ✅ app.js
import { formatDate } from "./lib.js";
formatDate();
Cause 2: wrong method for the value's real type
const data = await res.json(); // API returned { items: [...] }, not an array
data.map(x => x.id);
// ❌ data.map is not a function — data is an object, not an array
data.items.map(x => x.id); // ✅ call the method on the actual array
Cause 3: a variable shadowing the function
import { map } from "lodash";
function process(map) { // ❌ parameter named "map" shadows the import
return map(items, fn); // TypeError: map is not a function (it's the array you passed)
}
function process(list) { // ✅ don't reuse the imported name
return map(list, fn);
}
Cause 4: calling before it's assigned (hoisting)
doWork();
const doWork = function () { /* ... */ };
// ❌ ReferenceError first (TDZ) if truly before; but with `var` you'd get
// "doWork is not a function" because var hoists the binding as undefined
var doWork = function () { console.log("done"); };
doWork(); // ✅ call after the assignment has run
Worked example: a broken build
This often shows up only after bundling, not in local dev:
// works in dev (unminified), fails in production:
import { helper } from "./utils";
helper();
// Uncaught TypeError: helper is not a function
// Check: does the built/minified bundle still export "helper"?
// Common causes: tree-shaking dropped an export it thought was unused,
// or a CommonJS/ESM interop mismatch changed the shape of the import.
Common variants at a glance
| Message | Likely cause | Fix |
|---|---|---|
X is not a function (import) | default/named export mismatch | match the import style to the export |
X.map is not a function | X isn't actually an array | check the real shape, index into the array field |
| your own function, still fails | a shadowing variable/param | rename the local binding |
| works in dev, fails in build | bundler dropped/renamed the export | inspect the built output |
module.exports.X is not a function | CommonJS/ESM interop mismatch | align require/import style |
Debugging checklist
- ✓ Log
typeof Xright before the call - ✓ Importing a library? Check default vs named export in its docs/source
- ✓ Calling a method on data? Confirm the value's real shape/type first
- ✓ Same name used nearby? Check for a shadowing parameter or variable
- ✓ Only fails after build? Inspect the bundled output for the export
- ✓ TypeScript catches most of this at compile time — consider adopting it for a library boundary
Frequently Asked Questions
What does 'X is not a function' mean?
You wrote X(...) but the value bound to X at that point is not callable — it's undefined, an object, a string, or anything else that isn't a function. JavaScript only discovers this at the moment of the call, so the error always points at the exact line where you tried to invoke it.
Why does importing a library trigger this?
The most common cause is a default vs named export mismatch: import foo from 'lib' when the library only exports named bindings (import { foo } from 'lib'), or the reverse. You end up with the whole module object (or undefined) bound to foo, and calling foo() fails because foo isn't a function.
Why does a method that should exist say it's not a function?
Usually the value isn't the type you think it is — an array method called on an object, a string method called on a number, or the value came from JSON.parse and turned out to be null or a plain object instead of the class instance with that method. Log typeof value and the value itself right before the call.
Why does my own function say it's not a function?
A local variable or parameter with the same name is shadowing your function — declaring let map = [] in the same scope as a map() you meant to call, for example. Check for a same-named variable, parameter, or loop index declared between the function definition and the call site.
Why does it happen after minifying or bundling?
A build step can tree-shake an export it thinks is unused, target the wrong module format (CommonJS vs ESM interop), or bundle a stale cached chunk. Confirm the function still exists in the built output, and that your import/export syntax matches the bundler's expected module format.
How do I fix 'X is not a function' fast?
Log typeof X right before the call to see what it actually is. If it's undefined, check the import/export shape and spelling. If it's an object, you likely have the wrong value (module object instead of function, or a shadowing variable). Fix the source of the binding, not the call site.
More JavaScript & runtime errors
Browse the full reference for JavaScript, Node.js, and database errors — exact message, cause, and fix.