"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, 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.
- 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. - 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. - Log the parent, not the child.
console.log(res.data) or console.log(Object.keys(res.data)) shows what's really there. - 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 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 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 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 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 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 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 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() 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 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. 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 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 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 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.