Fix Common Errors

Fix 'undefined is not a function' in JavaScript

'undefined is not a function' usually means a typo, a scope issue, or a missing import. Learn to trace the call and fix the root cause fast.

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:

  1. Typo in the name. callbackFn() when you meant callbackFunction(), or this.fetchData() when the method is spelled differently.
  2. Missing import. You reference a utility or helper that was never imported, so the name is undefined in the current module.
  3. Scope collision. A local variable shadows a function, or you're inside a callback where this doesn't point where you expect.
  4. Async timing. You assign a function conditionally or fetch it dynamically, then try to call it before the assignment.
  5. Destructuring or object access wrong. You try to call a property of an object that doesn't exist, like config.handlers.onError() when handlers is 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.

Fix your next production error faster

Point any Sentry SDK at LightTrace — free up to 5,000 events/month.