# TypeError: Cannot read properties of undefined (reading 'x') in JavaScript: 10 Causes and Fixes

**"TypeError: Cannot read properties of undefined (reading 'x')" means you tried to read `.x` from a value that is `undefined`. The name in brackets is the property you asked for; the real problem is the thing *before* the dot. In `user.address.city`, if the error says `(reading 'city')`, then `user.address` is undefined. The fix is to find out why that value is missing (data not loaded yet, a function that forgot to `return`, a typo in a key, an array index past the end, or a lost `this`) and either fix the cause or guard it with optional chaining: `user.address?.city ?? "fallback"`.**

This is probably the most common JavaScript error on the planet. It shows up in plain browser scripts, in Node.js, in React and Next.js apps, and in TypeScript projects that compiled without complaint. It looks scary because it often appears deep inside a stack trace, but it always comes down to the same thing: something you expected to be an object or array turned out to be `undefined`.

I reproduced all 10 causes below on Node.js 20.19.2 and Google Chrome 156, plus React 19 and TypeScript 5.9, and pasted the real output. Each cause has the broken code, the exact error, and a tested fix you can copy.

**In this guide:**

- What the error actually means
- The same error in Chrome, Firefox, Safari and Node
- How to find which value is undefined
- Cause 1: A nested property doesn't exist
- Cause 2: The array index is past the end
- Cause 3: The data hasn't loaded yet (async code)
- Cause 4: React state starts as undefined
- Cause 5: The function forgot to return
- Cause 6: A typo or the wrong key name
- Cause 7: A lookup or find() returned nothing
- Cause 8: this is undefined inside a class method
- Cause 9: Destructuring a missing argument
- Cause 10: The DOM element isn't there yet (the null version)
- Optional chaining vs nullish coalescing vs if checks
- Catch it before it runs: TypeScript strictNullChecks
- Quick fix checklist
- Frequently asked questions

## What the error actually means

Here is the smallest possible version. The `user` object exists, but it has no `address` property, so `user.address` is `undefined`, and reading `.city` from `undefined` throws.

```
const user = { name: "Mahir" };
console.log(user.address.city);
```

Running it with `node c1.js` gives:

```
/workspace/seo-posts-oct31/test/js/c1.js:2
console.log(user.address.city);
                         ^

TypeError: Cannot read properties of undefined (reading 'city')
    at Object.<anonymous> (/workspace/seo-posts-oct31/test/js/c1.js:2:26)
```

Three clues are worth reading every time:

- **The property in brackets** (`'city'`) tells you what you were trying to read. The value to the left of it is the one that's undefined.
- **The caret `^`** points at the exact spot on the line where it broke.
- **The file and line number** (`c1.js:2:26` means line 2, column 26) take you straight to the code.

A close cousin is `Cannot read properties of null (reading 'x')`. Same idea, but the value is `null` instead of `undefined`. Most of the fixes below work for both.

## The same error in Chrome, Firefox, Safari and Node

The wording depends on the JavaScript engine, which is why you might see a different message from a user on another browser. Chrome, Edge, Node.js, Deno and Electron all use the V8 engine, so they share the message I tested above. According to [MDN's reference for this error](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Unexpected_type), the other engines phrase it like this:

| Engine | Where you'll see it | Message |
| --- | --- | --- |
| V8 | Chrome, Edge, Opera, Node.js | `TypeError: Cannot read properties of undefined (reading 'x')` |
| SpiderMonkey | Firefox | `TypeError: obj is undefined` |
| JavaScriptCore | Safari, iOS browsers | `TypeError: undefined is not an object (evaluating 'obj.x')` |

Older Chrome versions (before about 2021) said `Cannot read property 'x' of undefined`. If you're reading an old Stack Overflow answer with that wording, it's the same error.

## How to find which value is undefined

Before you change any code, spend one minute finding the culprit. Guessing is how people end up sprinkling `?.` everywhere and hiding real bugs.

1. **Go to the line in the stack trace.** The first line that points at *your* file (not `node:internal` or `react-dom`) is usually the one to look at.
2. **Look one step left of the property in brackets.** For `res.data.Results[0].name` with `(reading '0')`, the undefined value is `res.data.Results`.
3. **Log the parent, not the child.** `console.log(res.data)` or `console.log(Object.keys(res.data))` shows what's really there.
4. **Pause on the error.** In Chrome DevTools, open Sources and tick "Pause on uncaught exceptions". The debugger stops on the broken line with every variable visible. Google's [DevTools JavaScript debugging guide](https://developer.chrome.com/docs/devtools/javascript) walks through it.

Once you know which value is undefined, match it to one of the 10 causes below.

## Cause 1: A nested property doesn't exist

This is the example from the top. Some objects simply don't have every field: a user who never entered an address, an optional setting, a product with no discount. Reading two levels deep without checking breaks as soon as the middle level is missing.

**Fix:** use optional chaining (`?.`), which stops and returns `undefined` instead of throwing, and nullish coalescing (`??`) to supply a default.

```
const user = { name: "Mahir" };
console.log(user.address?.city);
console.log(user.address?.city ?? "No city saved");
```

```
undefined
No city saved
```

MDN's page on [optional chaining](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining) covers the other forms too: `obj?.[key]` for dynamic keys and `fn?.()` for functions that might not exist. Both work in every current browser and in Node 14 and later.

## Cause 2: The array index is past the end

Arrays don't throw when you read past the end; they quietly give you `undefined`. The error comes one step later, when you read a property from that missing item. The classic culprit is `<=` in a loop:

```
const posts = [{ title: "First" }, { title: "Second" }];
for (let i = 0; i <= posts.length; i++) {
  console.log(posts[i].title);
}
```

```
First
Second
TypeError: Cannot read properties of undefined (reading 'title')
```

Both posts print, then the loop runs once more with `i = 2`, and `posts[2]` is undefined.

**Fix:** use `<` instead of `<=`, or better, use `for...of` so there's no index to get wrong. For the last item, `at(-1)` is cleaner than `posts[posts.length - 1]`.

```
for (let i = 0; i < posts.length; i++) console.log(posts[i].title);
for (const post of posts) console.log(post.title);
console.log(posts.at(-1).title);
```

```
First
Second
First
Second
Second
```

## Cause 3: The data hasn't loaded yet (async code)

This one catches almost everyone who starts working with APIs. You start a request, and on the very next line you use the result. But the request hasn't finished, so the variable is still `undefined`.

```
let data; // filled in later by fetch
setTimeout(() => { data = { items: [{ id: 1 }] }; }, 100);
console.log(data.items.length);
```

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

**Fix:** use the data only after it has arrived. With `async`/`await`, the line after `await` doesn't run until the promise settles:

```
async function load() {
  const data = await new Promise(r => setTimeout(() => r({ items: [{ id: 1 }] }), 100));
  console.log(data.items.length);
}
load();
```

```
1
```

If promises still feel slippery, my [async/await tutorial](https://mfaysal.com/blog/javascript-async-await-tutorial) explains the order things run in, step by step.

## Cause 4: React state starts as undefined

React is the same async problem in a different outfit. If you call `useState()` with no argument, the first render happens with `undefined`, before your `useEffect` fetch has a chance to fill it in. I rendered this component with React 19.3 on the server to capture the error:

```
function Posts() {
  const [data] = useState();            // undefined until the fetch finishes
  return <ul>{data.items.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
}
```

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

**Fix:** give state a sensible starting value, or show a loading state until the data arrives. Both versions rendered cleanly:

```
// Option A: start with an empty array
const [items, setItems] = useState([]);
return <ul>{items.map(p => <li key={p.id}>{p.title}</li>)}</ul>;

// Option B: start with null and render a loading message
const [data, setData] = useState(null);
if (!data) return <p>Loading…</p>;
return <ul>{data.items.map(p => <li key={p.id}>{p.title}</li>)}</ul>;
```

```
<ul></ul>
<p>Loading…</p>
```

The [React useState reference](https://react.dev/reference/react/useState) calls this argument `initialState`. Matching its shape to what your component expects (an array for lists, `null` for "not loaded yet") prevents this error in Next.js pages too.

## Cause 5: The function forgot to return

A function without a `return` statement returns `undefined`. If you chain a property onto its result, you get the error, even though the object inside the function was perfectly fine.

```
function getConfig() {
  const config = { theme: { colour: "white" } };
  // forgot: return config;
}
console.log(getConfig().theme);
```

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

Arrow functions have a sneaky version of this. Curly braces start a function body, so the value inside is calculated and thrown away:

```
const double  = n => { n * 2 };   // body block: returns undefined
const double2 = n => n * 2;       // expression: returns the value
console.log(double(4), double2(4));
```

```
undefined 8
```

**Fix:** add the missing `return`, or drop the braces on one-line arrow functions. The same trap appears in `.map()` callbacks, where a missing return gives you an array full of `undefined`. Variables declared with `let` or `var` but never assigned start out as `undefined` too; my comparison of [var vs let vs const](https://mfaysal.com/blog/javascript-var-vs-let-vs-const) shows exactly when that happens.

## Cause 6: A typo or the wrong key name

JavaScript property names are case-sensitive, and the API you're calling doesn't care what you assumed. Here the response uses `results` but the code asks for `Results`:

```
const res = { data: { results: [{ name: "Ada" }] } };
console.log(res.data.Results[0].name);
```

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

Notice the message says `(reading '0')`. Array indexes count as property names too, so `Results[0]` is "read property 0 of `Results`".

**Fix:** print the real keys and copy them exactly. This takes five seconds and settles the argument:

```
console.log(Object.keys(res.data));
console.log(res.data.results[0].name);
```

```
[ 'results' ]
Ada
```

When you work with API responses a lot, the network tab in DevTools is even quicker: click the request, open "Response", and you'll see the exact shape. My guide to [sending and reading JSON with fetch](https://mfaysal.com/blog/javascript-fetch-post-json-example) has more on checking response shapes.

## Cause 7: A lookup or find() returned nothing

Looking something up by key or by search can come back empty. `obj[key]` gives `undefined` for a missing key, and `Array.prototype.find()` returns `undefined` when nothing matches.

```
const prices = { apple: { gbp: 0.5 } };
const fruit = "banana";
console.log(prices[fruit].gbp);   // c7.js

const rows = [];
const first = rows.find(r => r.id === 3);
console.log(first.name);          // c10.js
```

```
TypeError: Cannot read properties of undefined (reading 'gbp')
TypeError: Cannot read properties of undefined (reading 'name')
```

**Fix:** treat "not found" as a normal case and handle it on purpose. The [MDN find() page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find) spells out the `undefined` return, and it's worth remembering for `Map.get()` as well.

```
if (!(fruit in prices)) console.log(`No price for ${fruit}`);
console.log(prices[fruit]?.gbp ?? 0);

if (!first) { console.log("Row 3 not found"); } else { console.log(first.name); }
```

```
No price for banana
0
Row 3 not found
```

## Cause 8: this is undefined inside a class method

When you pass a class method somewhere as a plain function (an event handler, a `setTimeout`, a callback), it loses its connection to the object. Class bodies run in strict mode, so `this` becomes `undefined`, and the first `this.something` throws.

```
class Counter {
  constructor() { this.state = { count: 0 }; }
  increment() { this.state.count++; console.log(this.state.count); }
}
const c = new Counter();
const fn = c.increment;
fn();
```

```
TypeError: Cannot read properties of undefined (reading 'state')
    at increment (/workspace/seo-posts-oct31/test/js/c6.js:3:22)
```

**Fix:** bind the method in the constructor, or define it as an arrow function class field, which captures `this` automatically. [Function.prototype.bind()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) on MDN explains why the first option works.

```
// Option A: bind once in the constructor
constructor() { this.state = { count: 0 }; this.increment = this.increment.bind(this); }

// Option B: arrow function class field
class Counter2 { state = { count: 10 }; increment = () => { this.state.count++; console.log(this.state.count); }; }
```

```
1
11
```

## Cause 9: Destructuring a missing argument

Destructuring in a function's parameters is handy, but if the caller passes nothing, JavaScript tries to destructure `undefined`. V8 gives this its own, clearer message:

```
function greet({ name }) { return `Hi ${name}`; }
console.log(greet());
```

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

**Fix:** give the whole parameter a default of `{}`, and give each property its own default if you need one. The MDN page on [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring) has a section on default values.

```
function greet({ name = "friend" } = {}) { return `Hi ${name}`; }
console.log(greet());
console.log(greet({ name: "Mahir" }));
```

```
Hi friend
Hi Mahir
```

## Cause 10: The DOM element isn't there yet (the null version)

In the browser, `document.querySelector()` and `getElementById()` return `null` when they can't find the element. If your script sits in the `<head>`, the button doesn't exist yet when the script runs. I loaded this page in Chrome 156:

```
<head>
  <script>
    document.querySelector('#save').addEventListener('click', save);
  </script>
</head>
<body><button id="save">Save</button></body>
```

```
TypeError: Cannot read properties of null (reading 'addEventListener')
```

**Fix:** run the code after the HTML has been parsed. Add `defer` to the script tag, move the script to the end of `<body>`, or wait for the [DOMContentLoaded event](https://developer.mozilla.org/en-US/docs/Web/API/Document/DOMContentLoaded_event). Also double-check the selector: `#save` for an id, `.save` for a class.

```
document.addEventListener('DOMContentLoaded', () => {
  document.querySelector('#save').addEventListener('click', save);
});
```

The same `null` version appears with JSON, because `JSON.parse('{"user": null}')` gives you a real `null`, and `settings.user.email` then fails with `(reading 'email')`.

## Optional chaining vs nullish coalescing vs if checks

You now have several tools for guarding against undefined. They're not interchangeable, so here's when to reach for each:

| Tool | What it does | Use it when |
| --- | --- | --- |
| `a?.b` | Returns `undefined` instead of throwing if `a` is `null` or `undefined` | The value is genuinely optional |
| `a ?? "x"` | Uses `"x"` only if `a` is `null` or `undefined` | You want a default but `0` and `""` are valid values |
| `a || "x"` | Uses `"x"` for any falsy value (`0`, `""`, `false`, `NaN`) | Empty values should also fall back |
| `if (!a) return` | Stops early | Nothing useful can happen without the value |
| `= {}` default | Replaces a missing argument | Functions with optional options objects |

The difference between `??` and `||` matters more than people expect. I tested it:

```
const c = 0, d = "";
console.log(c ?? "fallback", d ?? "fallback", c || "fallback");
```

```
0  fallback
```

`??` kept the `0` and the empty string; `||` threw the `0` away. MDN's [nullish coalescing page](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing) has more edge cases. One warning: don't add `?.` to every dot "just in case". If a value should always exist, a crash tells you something is broken upstream, while `?.` silently hides it and the bug shows up somewhere harder to trace.

## Catch it before it runs: TypeScript strictNullChecks

If you use TypeScript, turn on `strict` (or at least `strictNullChecks`) and the compiler will flag most of these bugs before your code ever runs. I compiled cause 1 with TypeScript 5.9.3:

```
type User = { name: string; address?: { city: string } };
const user: User = { name: "Mahir" };
console.log(user.address.city);
```

```
$ npx tsc --strict --noEmit t1.ts
t1.ts(3,13): error TS18048: 'user.address' is possibly 'undefined'.
```

The fix is the same as in JavaScript (`user.address?.city` or an `if` check), but you find out at build time instead of from a user's bug report. The [strictNullChecks option](https://www.typescriptlang.org/tsconfig/strictNullChecks.html) is documented in the TypeScript handbook. TypeScript can't protect you from API data that doesn't match your types, though, so validate responses at the boundary when it matters.

## Quick fix checklist

| You see | Likely cause | Fix |
| --- | --- | --- |
| Error right after a fetch or API call | Data not loaded yet | `await` it, or render a loading state |
| Error only on the first render in React/Next.js | `useState()` with no initial value | `useState([])` or `useState(null)` plus a check |
| `(reading '0')` or `(reading 'length')` | Array is undefined (wrong key or not loaded) | Log the parent object's keys |
| Error on the last loop iteration | `<=` instead of `<` | Use `for...of` |
| `(reading 'state')` or `(reading 'props')` | Lost `this` | `bind` or arrow class field |
| `of null (reading 'addEventListener')` | Element not found or not loaded | `defer`, check the selector |
| Chained call on a function result | Missing `return` | Add `return` |

For quick everyday jobs like formatting JSON or encoding text while you debug, the browser-based [free tools on mfaysal.com](https://mfaysal.com/free-tools/) run without sign-up.

## Frequently asked questions

### What does "Cannot read properties of undefined" mean in simple terms?

It means you asked JavaScript for a property of something that doesn't exist. The value to the left of the dot is undefined, so there's nothing to read the property from. The name in brackets is the property you wanted, not the thing that's missing.

### How do I fix Cannot read properties of undefined (reading 'map')?

Your array is undefined, usually because API data hasn't arrived yet or the key name is wrong. In React, start state as an empty array with useState([]), or check that the data exists before calling map. Log the parent object to confirm the real key.

### Is Cannot read property of undefined the same error?

Yes. "Cannot read property 'x' of undefined" is the older Chrome and Node wording. Since around 2021, V8 says "Cannot read properties of undefined (reading 'x')". The cause and fixes are identical.

### What is the difference between undefined and null in this error?

Undefined usually means a value was never set, such as a missing key or a function with no return. Null is usually returned on purpose, for example by querySelector when nothing matches or in JSON data. Optional chaining and ?? handle both.

### Should I just use optional chaining everywhere?

No. Use ?. where a value is genuinely optional. If a value should always be there, letting the error happen (or throwing your own clear error) tells you about the real bug. Too much optional chaining hides problems until they surface somewhere harder to debug.

### Why does my code work locally but fail in production?

Usually the data is different. Production APIs can return empty lists, missing fields or slower responses, and real users have accounts without optional details filled in. Log the actual response in production or add a check for the missing case.

### Does try...catch fix this error?

It stops the crash, but it doesn't fix the cause. Wrap code in try...catch when you can do something useful on failure, such as showing an error message. For missing data, a simple check or a default value is clearer.

### Why does the error say reading '0'?

Array indexes are property names in JavaScript, so arr[0] means reading property 0. If you see (reading '0'), the array itself is undefined. Check the key name and whether the data has loaded.

*Last checked: 31 October 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-cannot-read-properties-of-undefined).*

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