CoreMesh

Cannot Read Properties of Undefined (Reading 'bind'): The Fix

Why JavaScript throws Cannot read properties of undefined (reading 'bind'), how to find the missing object, and guards, optional chaining and async fixes.

Cannot Read Properties of Undefined (Reading 'bind'): The Fix

Cannot read properties of undefined (reading 'bind') means your code ran something.bind(...) and something was undefined at that moment. The word in quotes is the property the engine tried to read, and bind is a method that only exists on functions. So the real bug is one step to the left of .bind: the function you expected to be there isn't. Usually it's a typo in a method name, a method defined on the wrong object, or a handler that hasn't been assigned yet when the binding runs.

The fix is to find out what that left-hand expression actually is, then either make sure it's defined (most common), guard it, or use optional chaining so the missing case is handled on purpose. The sections below walk through those in order, from the most direct fix to the defensive patterns for async code.

What the error means#

Here's the full message as V8 (Chrome, Edge, Node.js) prints it. Firefox and Safari word it differently, as the MDN page on this TypeError lists:

text
TypeError: Cannot read properties of undefined (reading 'bind')      (V8: Chrome, Edge, Node.js)
TypeError: "x" is undefined                                           (Firefox)
TypeError: undefined is not an object (evaluating 'obj.x')           (Safari)

Read it as three parts:

  • Cannot read properties of undefined tells you the object on the left of the dot was undefined. If it says null instead, same problem, different nullish value.
  • (reading 'bind') tells you which property was being read. It does not mean bind is broken. It means the thing you called bind on doesn't exist.
  • The stack trace underneath points to the file and line. That line contains the .bind( you need to look at.

MDN sums up the cause in one sentence: there was an unexpected type, most often an undefined or null value. You can't invoke a method on either of those. The engine doesn't know what you meant to bind, so it stops.

Find the undefined object behind 'bind'#

Go to the line from the stack trace and split the expression at the last dot before bind. Everything to the left is the suspect.

javascript
this.handleClick.bind(this)       // suspect: this.handleClick
service.api.request.bind(service) // suspect: service.api.request
handlers[name].bind(ctx)          // suspect: handlers[name]

Then check what the suspect evaluates to at that point. Three quick ways:

  1. Log it before the bind. A console.log(this.handleClick) on the line above is crude, but it answers the question in one reload.
  2. Set a breakpoint on the line in your browser's DevTools or in Node's inspector and hover the expression.
  3. Search the file for the exact name. If handleClick is only mentioned once, the method was never defined and you've found your typo.

While you're there, work out why it's undefined. The common reasons are:

Cause What it looks like
Misspelled method name this.handleClik.bind(this) when the method is handleClick
Method defined on another class or module Binding this.submit in a component that never declares submit
Method is an arrow property or static, not where you think static format() bound as this.format
Wrong key in a lookup handlers[action] where action is "SAVE" but the key is "save"
Object not loaded yet Binding a method from data that arrives later from fetch
Destructuring that missed const { onSave } = props when the parent never passed onSave

The last two are the async cases, and they get their own section below.

Fix 1: define the function or correct the name#

If the suspect should always exist, the right fix is to make it exist. Don't paper over a typo with a guard. Here's the most common shape of this bug in a class, followed by its fix:

javascript
class Button {
  constructor() {
    try {
      // Typo: the class has no method called handleClik
      this.onClick = this.handleClik.bind(this);
    } catch (err) {
      console.log("Caught:", err.message);
    }

    // Correct name, same line otherwise
    this.onClick = this.handleClick.bind(this);
  }

  handleClick() {
    return "clicked, this is a " + this.constructor.name;
  }
}

const button = new Button();
console.log(button.onClick());

The first attempt throws because this.handleClik is undefined. The second works because the name matches the declared method. In a React class component the pattern is identical: every this.x.bind(this) in the constructor needs a matching x() {} in the class body.

How to confirm it worked: the error disappears and the bound function actually runs. If you fixed a typo, search the codebase for the misspelled name once more, because the same typo often appears in two places.

If you'd rather not bind at all, a class field written as an arrow function keeps this without a bind call:

javascript
class Button {
  handleClick = () => {
    return this.label;
  };
}

That removes the .bind( line entirely, which removes the place the error could be thrown.

Fix 2: guard before you bind#

When the function is genuinely optional, such as a plugin hook or a callback a caller may or may not pass, check it first. MDN recommends testing for undefined and null explicitly:

javascript
if (hooks.onSave !== undefined && hooks.onSave !== null) {
  this.save = hooks.onSave.bind(this);
}

For functions I'd go one step further and check the type, because a non-function value with a bind property is rare but a string or number in the wrong slot is not:

javascript
if (typeof hooks.onSave === "function") {
  this.save = hooks.onSave.bind(this);
}

MDN also notes you can test plain truthiness with if (foo) when you're sure foo won't be another falsy value like "" or 0. For a function slot that's usually safe, but typeof reads clearer and costs nothing.

How to confirm it worked: the error is gone, and when the hook is missing your code takes the branch you intended rather than silently doing nothing. Add a log or a thrown error with a clear message in the else branch if a missing hook should be a bug.

Fix 3: optional chaining and a default#

Optional chaining (?.) short-circuits to undefined instead of throwing when the thing on its left is null or undefined. It works for property access, bracket access and function calls, per the MDN optional chaining reference. For a bind, you put it between the possibly-missing function and .bind:

javascript
const config = {
  hooks: {},
};

// hooks exists but onSave doesn't: no throw, result is undefined
const save = config.hooks.onSave?.bind(config);
console.log("save:", save);

// plugins doesn't exist at all: chain both steps
const plugin = config.plugins?.onLoad?.bind(config);
console.log("plugin:", plugin);

// Grouping breaks the short-circuit. This still throws.
try {
  const broken = (config.plugins?.onLoad).bind(config);
  console.log(broken);
} catch (err) {
  console.log("grouped:", err.message);
}

// Pair with ?? to supply a fallback function
const onLoad = config.plugins?.onLoad?.bind(config) ?? (() => "default loader");
console.log(onLoad());

Two details from that example matter in practice:

  • Every nullable step needs its own ?.. If config.plugins can be missing, config.plugins.onLoad?.bind() still throws on plugins. MDN is explicit about this: if the root object itself may be nullish, you have to put ?. at that position as well.
  • Parentheses end the chain. (a?.b).c evaluates a?.b to undefined, then reads .c on it and throws. MDN gives exactly this case. Keep the chain unbroken from the nullable step through to .bind.

The nullish coalescing operator (??) pairs naturally here. fn?.bind(ctx) ?? fallback gives you a bound function or a default without an if.

How to confirm it worked: the line no longer throws, and you've decided what undefined means downstream. If callers of save would themselves crash on undefined, you've only moved the error. That's what the ?? fallback is for.

Prevent it in async code#

The async version of this bug is the one that passes every test and then fails in production. The function you're binding comes from a response, a lazy import or a store that hasn't populated yet. The code binds it once, early, and that moment is before the data exists.

The rule is simple: don't bind anything that depends on awaited data until after the await, and validate what came back before you touch it.

javascript
async function loadHandler(registry, name) {
  // Stand-in for a fetch, dynamic import, or store lookup
  const entry = await Promise.resolve(registry[name]);

  const fn = entry?.handler;
  if (typeof fn !== "function") {
    throw new Error(`No handler registered for "${name}"`);
  }
  return fn.bind(entry);
}

const registry = {
  save: {
    label: "save-entry",
    handler() {
      return "saved by " + this.label;
    },
  },
};

loadHandler(registry, "save").then((run) => console.log(run()));
loadHandler(registry, "delete").catch((err) => console.log(err.message));

The missing delete entry produces an error that names the problem instead of the generic reading 'bind' message. That's the goal in async code: turn an engine error at an unhelpful line into your own error at the point where the assumption failed.

A few more habits that prevent the async flavour:

  • Bind inside the function that uses the value, not at module load. Module-level const run = api.method.bind(api) executes before any data exists.
  • In React, bind or wire handlers in render or in the handler itself, not in a constructor that runs before props have arrived. Data fetched in an effect is undefined on the first render, so a handler that binds data.callback has to tolerate that first pass. The same first-render gap causes server and client markup to differ, which is covered in the hydration mismatch fix guide.
  • Check what fetch actually returned. A failed request that your code treated as success leaves response.handler undefined. If the failure is cross-origin, the browser console will say so, and the CORS error header checklist shows which header is missing.
  • Give destructured callbacks a default. const { onSave = () => {} } = props means onSave.bind(this) never sees undefined.

Same error, different property: 'map', 'length', 'id', '0'#

The word in quotes changes, the diagnosis doesn't. Whatever is left of that property was undefined. The table below maps the common variants to what's usually missing.

Message ends with Left side was Typical cause
(reading 'bind') a function Misspelled or undeclared method, callback not passed
(reading 'map') an array API returned an object or nothing, state not initialized to []
(reading 'length') an array or string Same as map, or a split result on missing text
(reading '0') an array Indexing rows[0] when rows was never set
(reading 'id') an object Lookup by key missed, item not found, find() returned undefined
(reading 'split') a string Field missing from a record, undefined passed where text was expected
(reading 'text') an object Reading a nested field from an empty or error response

The fixes are the same three: define the value, guard it, or chain it. For arrays, a default of [] is the cheapest prevention:

javascript
const items = response?.data?.items ?? [];
items.map((item) => item.id);

MDN's own example shows the bracket form for array indexes: arr?.[42] returns undefined instead of throwing Cannot read properties of undefined (reading '42') when arr is missing.

Common mistakes and gotchas#

Fixing the symptom, not the cause. Wrapping a required method in ?. makes the error vanish and the feature silently stop working. Use optional chaining only where undefined is a legitimate state.

Mixing up null and undefined in guards. if (fn !== undefined) lets null through, and null.bind throws the same way with null in the message. Use != null (loose), typeof fn === "function", or ?., which handles both.

Expecting ?. to catch non-functions. obj.method?.() returns undefined if method is missing, but if method is a string, you get obj.method is not a function. Optional chaining checks for nullish, not for callability.

Using ?. on an undeclared variable. undeclaredVar?.prop throws a ReferenceError, not undefined. Optional chaining only helps once the variable exists.

Assigning through a chain. obj?.prop = 1 is a SyntaxError. Chains are for reading and calling, not writing.

Breaking the chain with parentheses. (a?.b).c throws when a is nullish. Covered above, but it's the one that surprises people who think they already added the operator.

Binding in a constructor before the data exists. The React and async case. The constructor runs once; props and fetched data arrive later.

Blaming the error on a product you're using. If you're a user seeing this in a web app or a desktop client rather than a developer writing it, the bug is in that app's JavaScript. Reloading, clearing the cache or updating the app sometimes clears a stale state, but the real fix has to come from the app's developers. Reporting the exact message, including the word in quotes, helps them find it.

Quick checklist#

  1. Open the stack trace line and find the .bind(.
  2. Everything left of that dot is the suspect. Log it or hover it in a breakpoint.
  3. Typo or missing method? Fix the name or declare it. Done.
  4. Legitimately optional? Guard with typeof fn === "function", or use fn?.bind(ctx).
  5. Nullable at more than one level? Put ?. at every nullable step and don't wrap any part in parentheses.
  6. Need a fallback? Append ?? defaultFn.
  7. Comes from awaited data? Move the bind after the await and validate the result before binding.
  8. Same message with 'map', 'length', '0', 'id'? Same steps, default arrays to [] and objects to {}.
  9. Rerun and confirm the bound function actually executes, not just that the error is gone.

Quick answers

What does cannot read properties of undefined mean?

The value on the left of the dot was undefined, so JavaScript couldn't read the property named in quotes from it. The property name tells you what you tried to read; the fix is to find out why the object before it doesn't exist.

How do I fix cannot read properties of null?

The same way as the undefined version. Null is the other nullish value, and optional chaining (?.) and a typeof check both handle it. If you guard with !== undefined only, null still gets through.

Why does this happen in React?

Most often a class constructor binds this.someMethod before that method is declared, or a handler reads props or fetched data on the first render when it's still undefined. Fix the method name, or move the bind after the data arrives and give destructured callbacks a default.

What's the difference between reading 'bind' and reading 'map'?

Only the property that was being read. With 'bind' a function was missing; with 'map' an array was missing. Both mean the thing before the dot was undefined, and both are fixed by defining, guarding or chaining it.

I see this error in an app I use, not in code I wrote. What can I do?

The bug is in that app's JavaScript, so you can't fix it directly. Reloading, clearing the cache or updating to the latest version sometimes clears a stale state; otherwise report the exact message, including the quoted property name, to the app's support.

Did this solve it?

One tap. No account.

Saved you an hour? Send a tip.

USDC or ETH on Base, Ethereum, Arbitrum and more. Keeps the notes free and the examples running.

Base

Network

0xc783bc5c3fa14cEC3f09A22284bC91C5AF0d59e1
Open in wallet · 5 USDC

On a computer? Scan the code with your phone wallet, or copy the address.

Pick the network your wallet or exchange uses. USDC works on all six; ETH on Base, Ethereum, Arbitrum and Optimism. Don't send on Tron or Solana: this address doesn't exist there. Tips are optional and unlock nothing; every guide stays free.

Discussion

Start the discussion

Indra Wijaya

Builds and runs AI agents, bots and content pipelines, and writes down what works and what broke.

About · Email · RSS

Keep reading

New notes, no spam.

One post at a time, only when there's something worth reading. Follow in any feed reader.

Subscribe via RSS