Cannot Read Properties of Undefined (tested)

TypeError: Cannot read properties of undefined (reading 'X') means your code tried to access property X on a value that is undefined. We reproduced the exact wording in current Node.js and headless Chromium, and tested the three most common causes and their fixes.

1. The exact error

We ran this in Node.js v24.21.0:

const obj = undefined;
console.log(obj.foo);

Result, copied verbatim from the thrown error's .message:

TypeError: Cannot read properties of undefined (reading 'foo')

The property name you tried to read is interpolated into the message in single quotes. Accessing a missing nested property gives the same shape:

const obj = {};
console.log(obj.foo.bar);
// TypeError: Cannot read properties of undefined (reading 'bar')

Here obj.foo is undefined (object has no foo), so reading .bar off it throws. The message always names the property being read, not the variable.

2. Old wording vs current wording

Older V8/Node versions phrased this differently: TypeError: Cannot read property 'foo' of undefined (singular "property", "of undefined" at the end). The current phrasing is Cannot read properties of undefined (reading 'foo') (plural "properties", property name moved into a trailing parenthetical). Per the V8 changelog this reword shipped in V8 7.4 / Node.js 12 (2019) to also cover cases where multiple properties are accessed in one expression. We did not have an old Node build on this box to reproduce the legacy string, so that date is not measured here — if you see the old wording, you are on Node <12 or an old browser engine.

3. What we measured, side by side

EnvironmentCodeExact message
Node.js v24.21.0undefined.fooCannot read properties of undefined (reading 'foo')
Node.js v24.21.0{}.foo.barCannot read properties of undefined (reading 'bar')
Node.js v24.21.0destructure missing nested keyCannot read properties of undefined (reading 'items')
Chromium 153 (page.evaluate)undefined.fooCannot read properties of undefined (reading 'foo')
Chromium 153 (page.evaluate){}.foo.barCannot read properties of undefined (reading 'bar')

Wording was identical between Node 24 and Chromium 153 in every case we tried. Both run on V8, so this is expected: the message comes from the engine, not from either host.

4. Fix 1: optional chaining

Optional chaining (?.) returns undefined instead of throwing when the left side is null or undefined. We tested it directly against the same throwing case:

const obj = undefined;
console.log(obj?.foo);   // undefined, no throw

This is the right fix when a missing value is expected and undefined is an acceptable result downstream. It does not fix the underlying reason the value is missing — it only stops the crash at that line.

5. Fix 2: guard a destructure

Destructuring a property that does not exist throws the same error, because the destructure itself reads a property off undefined:

const resp = { data: undefined };
const { data: { items } } = resp;
// TypeError: Cannot read properties of undefined (reading 'items')

We confirmed the message is identical to a plain property read: the engine does not distinguish destructuring syntax from dot access when reporting the error. Give the nested value a default to fix it:

const { data: { items } = {} } = resp;
console.log(items); // undefined, no throw

6. Fix 3 (and a gotcha): async functions and a missing await

We tested the common claim that "forgetting await" causes this error, and it is only half true. An un-awaited async function call returns a Promise object, not undefined. Reading a property that a Promise does not have just silently returns undefined — it does not throw:

async function getUser() { return undefined; }
const user = getUser();      // forgot await: user is a Promise
console.log(user.name);      // undefined, NO throw (Promise has no .name)

The error only fires once you await and the resolved value itself is undefined — for example a DB lookup or fetch that found nothing:

const user = await getUser(); // resolves to undefined
console.log(user.name);
// TypeError: Cannot read properties of undefined (reading 'name')

So a missing await usually produces quiet wrong behaviour (reading properties off a Promise, getting undefined back with no crash), while the loud crash comes from code that does await correctly but gets back undefined anyway, e.g. an API call that failed and returned nothing. Fix it by checking the resolved value before use, or giving the function a real fallback return.

7. Quick checklist

If the type error is instead a compile-time TypeScript complaint rather than a runtime crash, see TypeScript: is not assignable to type.

Frequently asked questions

What does 'reading X' mean in the error message?

X is the property name your code tried to access. In 'Cannot read properties of undefined (reading \'foo\')' the code did something like value.foo where value was undefined. We confirmed this in Node 24.21.0 and Chromium 153.

Why did I see 'Cannot read property X of undefined' instead?

That is the older V8 wording, used before V8 7.4 / Node.js 12 (per the V8 changelog, not measured on this box). The current wording is 'Cannot read properties of undefined (reading X)'. Functionally they mean the same thing.

Does optional chaining always fix this error?

It stops the crash on that line by returning undefined instead, which we verified with obj?.foo. It does not fix why the value is undefined in the first place, so check whether undefined is actually an acceptable result.

Does forgetting await cause this error?

Not directly, as we measured: an un-awaited async call returns a Promise object, and reading a missing property off a Promise returns undefined without throwing. The error appears once you await and the resolved value itself is undefined.

Is the error the same in the browser as in Node.js?

Yes. We tested identical code in Node.js v24.21.0 and headless Chromium 153 and got byte-for-byte the same message in every case, since both run on the V8 engine.

Written with AI assistance. Measured on 2026-10-02 with Node.js v24.21.0 (native, no transpiler) and headless Chromium 153.0.8010.12 via Playwright 1.63 on Linux. The history of the old V8 wording is per the Node.js/V8 changelogs, not measured. PlainHeaders is our extension; curl, Python, Node.js and Chromium are free.