# TypeError: x is not a function in JavaScript: 10 Causes and Fixes (Tested Examples)

**"TypeError: x is not a function" means you wrote** `x()`**, but when that line ran,** `x` **held something other than a function:** `undefined`**, a string, an object, an array-like list or a Promise. To fix it, find the name just before "is not a function", log** `typeof` **for it on the line above, and work out why it isn't the function you expected. Usually the cause is a typo, a method that doesn't exist on that type, a value that replaced the function, or an import that returned something different.**

The name in the message is your best clue. `data.map is not a function` tells you `data` exists but has no `map` method, so it probably isn't an array. MDN's reference page for the ["is not a function" error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Not_a_function) lists the same family of mistakes.

I reproduced every error below myself. The scripts ran on Node.js 24.22.1, and the core cases gave byte-identical output on Node.js 18.20.8, 20.20.2, 22.23.4 and 26.11.1. The DOM examples ran in Chrome 156, and the TypeScript checks used tsc 7.0.2.

**In this guide:**

- What the error message is telling you
- How to find the cause in under a minute

1. A typo or wrong capitalisation in the method name

2. Calling an array method on something that isn't an array

3. You overwrote the function with a value

4. Calling a function expression before it is assigned

5. A missing semicolon before ( or \[

6. Import and export don't match

7. Forgetting await on an async function

8. this points somewhere else inside a callback

9. A callback argument that is missing or isn't a function

10. The method doesn't exist in your Node.js or browser version

- Quick reference: causes and fixes
- Prevent it: TypeScript, optional chaining and typeof checks
- Wrapping up
- Frequently asked questions

## What the error message is telling you

V8, the engine in Chrome and Node.js, prints the expression you tried to call. That makes the message more useful than it first looks. I ran seven broken calls through one script:

```plaintext
const api = { user: { getName: 'Ana' }, list: () => ({}) };
const notFn = 42;
```

```plaintext
TypeError: api.user.getName is not a function
TypeError: api.list(...).map is not a function
TypeError: notFn is not a function
TypeError: notFn is not a function or its return value is not iterable
TypeError: api.user.missing is not a function
TypeError: Cannot read properties of undefined (reading 'call')
```

| Message shape | What it means |
| --- | --- |
| `obj.name is not a function` | `obj` exists, but `obj.name` is not a function (often `undefined`, or a string) |
| `fn(...).name is not a function` | The *return value* of `fn()` has no such method |
| `x is not a function or its return value is not iterable` | You destructured the result, as in `const [a, b] = x()`, and `x` isn't callable |
| `Cannot read properties of undefined (reading 'name')` | A different error: the object *before* the dot is `undefined` |

That last row trips a lot of people up. If the thing before the dot is missing, you get "Cannot read properties of undefined" instead, which I covered in [Cannot read properties of undefined: causes and fixes](https://mfaysal.com/blog/javascript-cannot-read-properties-of-undefined).

## How to find the cause in under a minute

1. **Read the name.** Copy everything before "is not a function". That expression is the thing that isn't callable.
2. **Jump to the line.** The first stack frame (for example `c05_asi.js:2:1`) gives the file, line and column.
3. **Log the type** on the line above the call. `console.log(typeof x, x)` tells you at once whether you have `undefined`, a string, an object or a Promise.
4. **Match it to a cause below.** `undefined` usually points to a typo or a missing export. An object points to a wrong method or a missing await.

```plaintext
typeof plugin.init: undefined
```

Here's what that check looked like for an object that simply has no `init` method. Once you know the type, the fix is usually obvious.

## 1\. A typo or wrong capitalisation in the method name

JavaScript is case-sensitive, so `toUppercase` and `toUpperCase` are different properties. The misspelled one is `undefined`, and calling `undefined` throws:

```plaintext
const name = 'mahir';
try { console.log(name.toUppercase()); } catch (e) { console.log(e.constructor.name + ': ' + e.message); }
console.log(name.toUpperCase());
```

```plaintext
TypeError: name.toUppercase is not a function
MAHIR
```

The DOM has its own classic typos. Both of these came from Chrome 156:

```plaintext
TypeError: document.getElementByID is not a function
TypeError: document.getElementById(...).addEventlistener is not a function
```

**Fix:** check the exact spelling. It's `getElementById` (lowercase "d") and `addEventListener` (capital "L"). Your editor's autocomplete, or TypeScript (see below), catches these before you run anything.

## 2\. Calling an array method on something that isn't an array

`map`, `filter` and `forEach` live on arrays. API responses often wrap the array inside an object, and plain objects and strings don't have those methods:

```plaintext
const data = { users: [{ name: 'Ana' }, { name: 'Ben' }] };
const scores = { ana: 9, ben: 7 };
const csv = 'a,b,c';
```

```plaintext
TypeError: data.map is not a function
TypeError: scores.forEach is not a function
TypeError: csv.map is not a function
```

**Fix:** reach the actual array, or convert first:

```plaintext
console.log(data.users.map(u => u.name));
console.log(Object.entries(scores).map(([k, v]) => `${k}=${v}`).join(', '));
console.log(csv.split(',').map(x => x.toUpperCase()));
```

```plaintext
[ 'Ana', 'Ben' ]
ana=9, ben=7
[ 'A', 'B', 'C' ]
Array.isArray(data): false | Array.isArray(data.users): true
```

In the browser, `querySelectorAll()` returns a NodeList and `getElementsByClassName()` returns an HTMLCollection. Neither is a real array:

```plaintext
TypeError: document.querySelectorAll(...).map is not a function
TypeError: document.getElementsByClassName(...).forEach is not a function
typeof document.querySelectorAll('.item').forEach -> "function"
Array.from(document.querySelectorAll('.item')).map(li => li.textContent) -> ["One","Two"]
[...document.getElementsByClassName('item')].map(li => li.textContent) -> ["One","Two"]
```

A NodeList does have `forEach`, but not `map` or `filter`. An HTMLCollection has neither. Wrap either one in `Array.from()` or spread it into `[...]`. If you're new to these methods, my guide to [map, filter and reduce](https://mfaysal.com/blog/javascript-map-filter-reduce) shows what each one returns.

## 3\. You overwrote the function with a value

A function name is just a variable. Assign something else to it and the function is gone. This one is easy to write when a result and a function share a name:

```plaintext
function total(items) { return items.reduce((s, n) => s + n, 0); }
let prices = [5, 10];
total = total(prices);          // the function is now the number 15
```

```plaintext
TypeError: total is not a function
sumOfPrices = 15 | sum still function
```

**Fix:** give results and functions different names, such as `sum()` and `sumOfPrices`. Declaring functions with `const` also helps, because reassigning a `const` throws straight away at the line that caused the damage, not later at the call.

The same thing happens with globals. A variable called `name`, `status` or `close` in a browser script can clash with built-in `window` properties.

## 4\. Calling a function expression before it is assigned

Function *declarations* are hoisted completely, so you can call them above where they appear. A function *expression* stored in `var` is not. Only the variable is hoisted, holding `undefined`:

```plaintext
try { greet('Ana'); } catch (e) { console.log('TypeError: ' + e.message); }
var greet = function (n) { return 'Hi ' + n; };
console.log(hello('Ben'));
function hello(n) { return 'Hello ' + n; }
```

```plaintext
TypeError: greet is not a function
Hello Ben
```

With `const` or `let`, the same mistake gives a clearer error:

```plaintext
ReferenceError: Cannot access 'greet' before initialization
```

**Fix:** move the call below the assignment, or use a function declaration. My post on [var vs let vs const](https://mfaysal.com/blog/javascript-var-vs-let-vs-const) explains hoisting and the temporal dead zone in more detail.

## 5\. A missing semicolon before ( or \[

Automatic semicolon insertion doesn't add a semicolon before a line that starts with `(` or `[`. JavaScript glues the two lines together and tries to call the previous value:

```plaintext
const config = { debug: true }
(function () { console.log('setup ran'); })()
```

```plaintext
TypeError: {(intermediate value)} is not a function
```

The same thing happens with an array literal on the next line. This one reads `"hello"[1, 2]` as an index lookup, gets the character `"l"`, and then calls `forEach` on it:

```plaintext
const greeting = 'hello'
[1, 2].forEach(n => console.log(n))
```

```plaintext
TypeError: "hello"[(1 , 2)].forEach is not a function
```

**Fix:** end the previous statement with a semicolon. If your style skips semicolons, start lines beginning with `(` or `[` with `;`. A formatter like Prettier also handles this for you.

```plaintext
const config = { debug: true };
(function () { console.log('setup ran', config.debug); })();
```

```plaintext
setup ran true
```

MDN's [lexical grammar page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar) lists exactly where semicolons get inserted and where they don't.

## 6\. Import and export don't match

A very common cause in Node.js projects is destructuring a named export from a module that exports a single function, or the reverse:

```plaintext
module.exports = formatPrice;
exports.formatPrice = (n) => '£' + n.toFixed(2);
```

```plaintext
const { formatPrice } = require('./mods/format');
const formatPrice2 = require('./mods/named');
```

```plaintext
TypeError: formatPrice is not a function
TypeError: formatPrice2 is not a function
format.js exports a function | named.js exports an object
```

In ES modules, calling a namespace import (`import * as utils`) as if it were the function fails the same way. A missing default export fails earlier, at load time, with a SyntaxError:

```plaintext
TypeError: utils is not a function
SyntaxError: The requested module './utils.mjs' does not provide an export named 'default'
```

**Fix:** open the module and check what it really exports, then match it: `require('./format')` for `module.exports = fn`, and `{ formatPrice }` for named exports. Logging `typeof require('./module')` settles it in one line.

Circular imports cause a sneakier version. When two CommonJS files require each other, one of them gets an *unfinished* exports object while it loads:

```plaintext
TypeError (during load): a.hello is not a function
(node:154628) Warning: Accessing non-existent property 'hello' of module exports inside circular dependency
```

Node.js prints that warning to help. The fix is to move the shared code into a third module, or to export before you require. The [Node.js modules documentation](https://nodejs.org/api/modules.html) has a section on cycles.

## 7\. Forgetting await on an async function

An `async` function always returns a Promise, even when the code inside returns an array. Without `await`, you call array methods on the Promise:

```plaintext
async function getUsers() { return [{ name: 'Ana' }, { name: 'Ben' }]; }
const users = getUsers();
```

```plaintext
TypeError: users.map is not a function
users is [object Promise]
```

The reverse happens with `fetch()`: once you've awaited `res.json()`, the result is plain data, so calling `.json()` on it again fails. I used a small stand-in object for the Response so the test runs offline:

```plaintext
const body = await res.json();
```

```plaintext
TypeError: body.json is not a function
```

**Fix:** `await` the call inside an `async` function (or use `.then()`), and only call `.json()` on the Response object. My [async/await tutorial](https://mfaysal.com/blog/javascript-async-await-tutorial) covers the patterns step by step.

```plaintext
[ 'Ana', 'Ben' ]
```

## 8\. this points somewhere else inside a callback

A regular `function` callback gets its own `this`. In sloppy-mode scripts, that `this` is the global object, which has no `double` method:

```plaintext
doubledOld() { return this.items.map(function (n) { return this.double(n); }); },
doubledNew() { return this.items.map((n) => this.double(n)); },
```

```plaintext
TypeError: this.double is not a function
[ 4, 6 ]
```

In strict mode or ES modules, `this` is `undefined` instead, so the identical code throws a different error:

```plaintext
TypeError: Cannot read properties of undefined (reading 'double')
```

**Fix:** use an [arrow function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions), which keeps the outer `this`, as `doubledNew()` does. When you pass a method as a callback, for example to `addEventListener` or `setTimeout`, bind it with `this.method.bind(this)` or wrap it in an arrow function.

## 9\. A callback argument that is missing or isn't a function

If a function expects a callback and you forget to pass one, or you pass a string by mistake, the error points *inside* that function, which can be confusing:

```plaintext
function loadData(onDone) {
const data = [1, 2, 3];
onDone(data);
}
```

```plaintext
TypeError: onDone is not a function
TypeError: onDone is not a function
```

Both `loadData()` and `loadData('done')` failed with the same message. **Fix:** pass a real function. In your own code, give the parameter a default or check it up front so the error explains itself:

```plaintext
function loadSafe(onDone = () => {}) { onDone([1, 2, 3]); return 'ok'; }
if (typeof onDone !== 'function') throw new TypeError(`onDone must be a function, got ${typeof onDone}`);
```

```plaintext
ok
TypeError: onDone must be a function, got string
```

## 10\. The method doesn't exist in your Node.js or browser version

Newer built-ins simply don't exist on older runtimes. Your code is correct, but the method is `undefined`. I ran the same script on five Node.js versions:

```plaintext
v18.20.8 Object.groupBy: TypeError: Object.groupBy is not a function
v18.20.8 toSorted: TypeError: nums.toSorted is not a function
v20.20.2 Object.groupBy: TypeError: Object.groupBy is not a function
v20.20.2 toSorted: 1,2,3
v22.23.4 Object.groupBy: red,blue
```

| Method | Node 18.20.8 | Node 20.20.2 | Node 22.23.4, 24.22.1, 26.11.1 |
| --- | --- | --- | --- |
| `Object.groupBy()` | is not a function | is not a function | works |
| `Array.prototype.toSorted()` | is not a function | works | works |
| `Set.prototype.union()` | is not a function | is not a function | works |

**Fix:** run `node -v` and upgrade, or use an older equivalent (`[...nums].sort()` instead of `toSorted()`, `reduce()` instead of `Object.groupBy()`). For browser code, check the compatibility table at the bottom of the MDN page, such as [Object.groupBy()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/groupBy), or add a polyfill.

## Quick reference: causes and fixes

| # | Cause | Typical message | Fix |
| --- | --- | --- | --- |
| 1 | Typo or wrong case | `name.toUppercase is not a function` | Fix the spelling, use autocomplete |
| 2 | Not an array | `data.map is not a function` | Use the inner array, `Object.entries()`, `Array.from()` |
| 3 | Function overwritten | `total is not a function` | Separate names, `const` |
| 4 | Called before assignment | `greet is not a function` | Move the call, or use a declaration |
| 5 | Missing semicolon | `{(intermediate value)} is not a function` | Add `;` before `(` or `[` |
| 6 | Import/export mismatch | `formatPrice is not a function` | Match named vs default export |
| 7 | Missing await | `users.map is not a function` | `await` the Promise |
| 8 | Lost `this` | `this.double is not a function` | Arrow function or `bind()` |
| 9 | Missing callback | `onDone is not a function` | Pass a function, add a default |
| 10 | Old runtime | `Object.groupBy is not a function` | Upgrade or use an older method |

## Prevent it: TypeScript, optional chaining and typeof checks

TypeScript catches the first and third causes before the code ever runs. Here's what tsc 7.0.2 reported for a typo and for calling a number:

```plaintext
check.ts(2,11): error TS2551: Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?
check.ts(4,1): error TS2349: This expression is not callable.
Type 'Number' has no call signatures.
```

For functions that might genuinely be missing, such as optional plugin hooks, use [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) with a call: `plugin.init?.()` calls `init` only if it exists, and returns `undefined` otherwise. A `typeof` check does the same thing more explicitly:

```plaintext
console.log('optional call:', plugin.init?.());
if (typeof plugin.init === 'function') plugin.init(); else console.log('init skipped: not a function');
```

```plaintext
optional call: undefined
init skipped: not a function
```

Don't wrap every call like this, though. If a function *should* exist, a loud error is more useful than silently skipping it. Keep `?.()` for things that are optional by design.

## Wrapping up

"x is not a function" always comes down to one question: what is `x` right now? Log its type, read the name in the message, and the cause is usually one of the ten above. The three I run into most often are a missing await, a NodeList treated as an array, and an import that returns something different from what I expected.

If you like small, practical browser utilities, have a look at my [free tools](https://mfaysal.com/free-tools/).

## Frequently asked questions

### What does "TypeError: x is not a function" mean?

It means you used parentheses to call something that isn't a function when that line ran. The value might be undefined, a string, an object, an array-like list or a Promise. Log typeof x just before the call to see which.

### Why do I get "map is not a function"?

The value isn't an array. It is often an object that wraps the array (use data.items.map), a string (split it first), a NodeList (use Array.from), or a Promise you forgot to await.

### How is this different from "Cannot read properties of undefined"?

"x.y is not a function" means x exists but x.y isn't callable. "Cannot read properties of undefined (reading 'y')" means x itself is undefined, so JavaScript can't even look up y.

### Why does querySelectorAll().map not work?

querySelectorAll returns a NodeList, not an array. It has forEach but no map or filter. Convert it with Array.from(list) or \[...list\] first.

### Why does a missing semicolon cause "is not a function"?

When the next line starts with ( or \[, JavaScript doesn't insert a semicolon, so it joins the two lines and tries to call the previous value. Adding a semicolon at the end of the previous line fixes it.

### Why does Object.groupBy say it is not a function?

Object.groupBy only exists in newer runtimes. In my tests it failed on Node.js 18.20.8 and 20.20.2 and worked on 22.23.4 and later. Upgrade Node.js or group with reduce().

### Can try...catch fix "is not a function"?

It can stop the crash, but it doesn't fix the cause. Use try...catch to handle errors you expect, and fix the underlying typo, import or missing await instead of hiding it.

### Does TypeScript prevent this error?

It prevents most of it. TypeScript flags misspelled methods (TS2551) and calls on non-callable values (TS2349) at compile time, but it can't help with untyped data from an API or with code that bypasses the types using any.

*Last checked: 3 November 2026. Figures, settings and guidance can change, so confirm anything important with the sources linked above.*

---

*Originally published on* [*mfaysal.com*](https://mfaysal.com/blog/javascript-typeerror-is-not-a-function)*.*

I'm learning cybersecurity on TryHackMe — follow my progress at [tryhackme.com/p/mfaysal.dev](https://tryhackme.com/p/mfaysal.dev).
