TypeError: “is not a function”, explained and fixed

This error means the code tried to call something as a function, with ( ) after it, and the value in that spot was not a function at all. It was undefined, a string, a plain object, or a method that does not exist on that type. The message names the expression that failed, and the fix is always to work out what that value actually is at runtime and why it is not the function the code expected.

How to find the bad call

  1. Read the start of the message. TypeError: user.greet is not a function says the object exists, but it has no callable greet. TypeError: undefined is not a function (common in minified builds) means the thing being called was never assigned at all.
  2. Log the value and its type at the failing line: console.log(typeof thing, thing). The output tells you instantly whether it is undefined, an object, or a string wearing a function's name.
  3. Check the stack trace top frame for the file and line, then look at where that value came from: an import, a property, a callback argument, or the result of another function.

The usual causes, most common first

  1. A method that does not exist on this type. Classic case: calling an array method like .map() or .forEach() on a plain object or a string, often because an API returned an object where an array was expected.
  2. A typo or a renamed method. The function exists under a slightly different name, or a library changed the name between versions.
  3. A bad import. Calling the module object instead of its export, mixing default and named imports, or importing a non-function such as a constant or a component's props.
  4. Variable shadowing. A local variable, parameter, or destructured property has the same name as a function defined elsewhere, and the local one wins.
  5. Calling the result of a function that returns nothing, or chaining onto a method that returns undefined instead of the object.
  6. An optional value used unconditionally, a callback prop or plugin hook that was never provided, then invoked as if it were guaranteed.

The fixes, in order

  1. Print the actual object and look at its real properties: console.log(Object.keys(obj)) settles most cases in seconds, the method is missing, renamed, or one level deeper than expected.
  2. Fix the data shape at the source. If an API sometimes returns an object instead of an array, normalize it where it is fetched, not at every call site.
  3. Correct the import form: import fn from './x' for a default export, import { fn } from './x' for a named one, and call the export, not the namespace.
  4. Guard only genuinely optional callbacks: onDone?.() is right for an optional hook and wrong as a blanket over every call, it hides the bug the same way optional chaining does elsewhere.
Renaming things until it stops throwing is not a fix. This error is the runtime telling you the value is a different type than the code assumes. Find the assumption that is wrong, upstream data, a version change, a shadowed name, and the correct call usually becomes obvious.

Common questions

What does “(intermediate value) is not a function” mean? Minified or transpiled code rewrote the call, so the original name is lost. It is the same error: something that is not a function was called. Debug it in the unminified build or with source maps enabled.

Why does it fail in production but not in development? Usually minification removed the names, making it harder to read, combined with real data arriving in a shape the test data never had. Reproduce with production-shaped data and check the type of the value at the failing call.

Is “not a function” the same as “is not defined”? No. Not defined (a ReferenceError) means the name itself does not exist. Not a function means the name exists but holds a value that cannot be called.

Related errors

Cannot read properties of undefined · ReferenceError: is not defined · Node: Cannot find module