Skip to content

Fix: TypeError: Cannot destructure property 'x' of 'y' as it is undefined

FixDevs ·

Part of:  React & Frontend Errors

Quick Answer

Fix JavaScript 'Cannot destructure property of undefined' from a missing argument, a failed API response, or optional chaining that doesn't prevent it.

The part that trips people up isn’t the error itself, it’s that obj?.value looks like it should have fixed this and didn’t. In my experience that one gotcha accounts for a good share of the “I already added optional chaining, why is this still crashing” questions I’ve seen. Optional chaining protects the property lookup that happens before the destructuring, not the destructuring itself, and those are two different steps in the language. This covers the exact wording across browsers, why it’s specifically null/undefined and nothing else that triggers it, and the fixes for the handful of places this actually comes from.

Cannot destructure property ‘x’ of ‘y’ as it is undefined

Chrome, Edge, and Node.js (all V8) throw this:

TypeError: Cannot destructure property 'name' of 'user' as it is undefined.
    at greet (app.js:3:18)

If the source isn’t a named variable, for example the object was inlined or came from a chained call, the middle part reads undefined or null literally instead of a name:

TypeError: Cannot destructure property 'name' of 'undefined' as it is undefined.

Firefox reports the same failure with completely different wording:

TypeError: undefined has no properties

Safari’s phrasing is different again:

TypeError: Right side of assignment cannot be destructured

All three are the same bug: destructuring syntax, const { a } = obj or a destructured function parameter, ran against a value that was null or undefined.

Why it’s specifically null and undefined, nothing else

Destructuring an object needs to look up a property on it, and JavaScript’s spec has a step called RequireObjectCoercible that runs first: it throws immediately if the value is null or undefined, before any property lookup happens. Every other value in JavaScript, including the other falsy ones, 0, '', false, NaN, gets boxed into a wrapper object and destructures without error, just producing undefined for whatever property you asked for:

const { length } = '';        // 0, no error, '' becomes a String wrapper
const { toFixed } = 0;        // a function, no error, 0 becomes a Number wrapper
const { anything } = false;   // undefined, no error
const { anything } = null;        // TypeError
const { anything } = undefined;   // TypeError

That is the entire rule. If you remember nothing else, remember that this error means one specific value, somewhere in the chain, was null or undefined at the moment it got destructured.

Fix missing function arguments with a default parameter

The single most common cause: a function destructures its parameter directly, and gets called with nothing:

// BUG: no default, so calling greet() with no argument throws
function greet({ name }) {
  console.log(`Hello, ${name}`);
}
greet();   // Cannot destructure property 'name' of 'undefined' as it is undefined
// FIX: default the whole parameter to an empty object
function greet({ name } = {}) {
  console.log(`Hello, ${name ?? 'stranger'}`);
}
greet();   // "Hello, stranger"

The = {} matters specifically because it only applies when the argument is undefined, exactly the case that would otherwise throw. It does not help if someone explicitly passes null, since a default parameter only kicks in for a missing or undefined argument, not a null one:

greet(null);   // still throws: a default value never applies here

Guard for that case explicitly if null is a realistic input, for example a value read from a database or an optional route param:

function greet(person) {
  const { name } = person ?? {};
  console.log(`Hello, ${name ?? 'stranger'}`);
}
greet(null);   // "Hello, stranger"

Fix API responses before destructuring them

Destructuring a fetch or database result immediately, before confirming it has the shape you expect, is the second most common cause:

// BUG: assumes the API always returns { data: [...] }
async function loadItems() {
  const response = await fetch('/api/items');
  const { data } = await response.json();   // throws if the API returned an error body instead
  return data;
}

An API that returns { error: 'not found' } on a 404, with no data key at all, does not throw here, data would just be undefined on that object. The crash this article is about needs the parsed body to be null specifically, JSON’s only representation of “nothing”, which some APIs genuinely return for an endpoint that found no matching record. A response with a genuinely empty body is a related but different failure: .json() itself rejects with a SyntaxError before your destructuring line ever runs, see Fix: Unexpected token in JSON for that one specifically:

// FIX: check the response, then guard the parsed body itself
async function loadItems() {
  const response = await fetch('/api/items');
  if (!response.ok) throw new Error(`Request failed: ${response.status}`);
  const body = await response.json();
  return body?.data ?? [];   // ?. guards a null body, ?? supplies the fallback
}

Why optional chaining alone doesn’t fix this

This is the gotcha that sends people back to search after they thought they’d already fixed it. Optional chaining short-circuits a property access to undefined, it does not do anything about destructuring that value afterward:

// STILL BROKEN: optional chaining protects the .profile access,
// but the destructuring of THAT result still runs unguarded
const { name } = user?.profile;   // throws when user.profile is undefined

user?.profile correctly evaluates to undefined instead of throwing when user is missing, that part works. But then const { name } = undefined is a completely separate operation, and optional chaining has no syntax that reaches into it. Fix it by defaulting the fallback with ??, not by adding more ?.:

// FIX: nullish-coalesce a fallback object for the destructuring itself
const { name } = user?.profile ?? {};

Fix React props and hooks

The same pattern shows up constantly in React, usually from a prop that wasn’t passed, or a hook value read before it’s ready:

// BUG: crashes if <UserCard /> is ever rendered without a user prop
function UserCard({ user }) {
  const { name, email } = user;
  return <div>{name}, {email}</div>;
}
// FIX: default the prop, or guard before destructuring
function UserCard({ user = {} }) {
  const { name, email } = user;
  return <div>{name ?? 'Unknown'}, {email ?? ''}</div>;
}

useContext is a frequent source of this too, when a component consumes a context outside its provider, or the provider hasn’t initialized its value yet:

const { theme } = useContext(ThemeContext);   // throws if ThemeContext's default value is undefined

If a context has no meaningful default, give it a real default object instead of leaving it undefined, or check for the provider’s presence explicitly rather than assuming every consumer is wrapped correctly.

Array destructuring fails differently

If the value on the right is meant to be an array instead of an object, null/undefined produce a different error entirely, because array destructuring requires an iterable, not a property lookup:

const [first] = getItems();   // TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))

Same root cause, getItems() returned nothing, but a different message because object and array destructuring fail through two different mechanisms internally. If you see “is not iterable” instead of “cannot destructure”, you are looking at the array form of this exact bug, and the fix is the same shape: default to an empty array.

const [first] = getItems() ?? [];

Still not working?

Nested destructuring only reports the first broken level. const { a: { b } } = obj throws at whichever level is null/undefined first, if obj.a is undefined, the error names a, not b, even though b is the property you were actually trying to reach. I’ve spent a good ten minutes staring at the wrong property before realizing the error was naming the parent, not the child I assumed it meant. Read the error’s property name literally, it tells you exactly which link in the chain broke, not the one furthest from the source.

The value is present in dev tools but still throws. Async timing is the usual explanation, destructuring a value before an async call that assigns it has resolved, a state variable read on the render before an effect populates it, or a websocket payload accessed before the connection handler ran. Add a loading guard rather than assuming the value exists just because it will exist eventually:

if (!data) return null;   // guard before the destructure that follows
const { items } = data;

TypeScript didn’t catch this at compile time. If the destructured type is typed as optional (user?: User) or the value comes from any/an un-narrowed union, TypeScript will not flag the missing runtime check. Narrow the type first (if (!user) return) before destructuring it, the compiler will then enforce the same guard everywhere else that variable is used.

It only happens for some users, not others. This usually points to conditional data, a feature flag, an A/B test branch, or a permissions check that changes whether an API includes a given field. Reproduce with the same account or role that triggers it rather than assuming the bug is timing-related.

For related undefined and prop-handling errors, see Fix: TypeError: Cannot read properties of undefined, Fix: React Cannot Read Property map of Undefined, Fix: Express req.body Undefined, and Fix: Environment Variable Undefined.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles