Quick answer
You wrote obj.a.b = value, but obj.a is undefined — there's no object there to set .b on. This is the write-side version of Cannot read properties of undefined: every intermediate level in a nested write must already exist. Initialize the missing parent to {} (or []) before writing to it.
The exact error string
const result = {};
result.stats.count = 1;
// Uncaught TypeError: Cannot set properties of undefined (setting 'count')
// -> result.stats is undefined, so there's nowhere to put "count"
What it actually looks like
?. guards reads, and a write still needs the parent to exist.Read vs write: why this one always throws
Reading result.stats when it doesn't exist just gives you undefined — you can keep going (and chain into a crash later, as in the "reading" version of this error). Writing has no such fallback: JavaScript needs a real object to attach the new property to, and if the parent is undefined, there's nowhere to put it, so it throws immediately at the assignment.
Cause 1: writing into a nested path that isn't built yet
const config = {};
config.server.port = 3000;
// ❌ config.server is undefined
const config = {};
config.server = config.server || {}; // ✅ initialize the parent first
config.server.port = 3000;
// or build the shape up front:
const config = { server: { port: 3000 } }; // ✅
Cause 2: grouping array items by a dynamic key
const groups = {};
for (const item of items) {
groups[item.category].push(item);
// ❌ groups[item.category] is undefined the first time this category appears
}
const groups = {};
for (const item of items) {
groups[item.category] = groups[item.category] || []; // ✅ initialize the bucket
groups[item.category].push(item);
}
Cause 3: mutating nested React/Redux state directly
// ❌ direct mutation, and settings may not exist on the current state
state.user.settings.theme = "dark";
// ✅ build a new object, spreading each level you touch
setState(prev => ({
...prev,
user: {
...prev.user,
settings: { ...prev.user.settings, theme: "dark" },
},
}));
Cause 4: a typo'd or wrong-cased intermediate key
const data = { userProfile: {} };
data.userprofile.name = "Ada";
// ❌ "userprofile" (lowercase p) doesn't exist — data.userProfile does
data.userProfile.name = "Ada"; // ✅ match the actual key exactly
Fix: nullish-assignment shortcut
Modern JavaScript's ??= operator initializes a value only if it's currently null/undefined, which is a compact way to guarantee the parent exists right before you write to it:
const config = {};
config.server ??= {}; // sets {} only if config.server is null/undefined
config.server.port = 3000; // ✅ now safe
Optional chaining cannot fix this one
The reflex for the reading version of this error is optional chaining, and it works perfectly there. Reaching for it on the write side does not just fail at runtime — the file will not parse:
const value = config?.database?.host; // ✅ reading — undefined, no throw
config?.database.host = "localhost";
// ❌ SyntaxError: Invalid left-hand side in assignment
Verified on Node v24.15.0. This is deliberate, and the reasoning is worth knowing: ?. means "stop and produce undefined if this is nullish", and there is no sensible way to assign a value to undefined. The specification therefore forbids an optional chain anywhere on the left of an assignment. A SyntaxError rather than a TypeError is the clue — the whole module fails to load, and the line number points at the assignment.
Build the path instead: the ??= one-liner
Since you cannot guard the write, you have to guarantee the parent exists first. Logical nullish assignment (??=) does this in one expression per level, and it only writes when the target is null or undefined, so existing data is never clobbered:
const config = {};
config.database ??= {}; // create the parent only if missing
config.database.host = "localhost"; // ✅ safe now
// config is { database: { host: "localhost" } }
For the grouping case — building { [key]: [...] } from a list — the same pattern collapses the usual four-line if into one, and Object.groupBy() replaces it entirely where your runtime has it:
// before: throws on the first unseen key
byStatus[order.status].push(order); // ❌ Cannot set properties of undefined
// after:
(byStatus[order.status] ??= []).push(order); // ✅ creates the array on first use
// or, Node 21+ / Chrome 117+:
const byStatus = Object.groupBy(orders, o => o.status);
Where the path is deep and comes from configuration rather than code, a Map or a single flat key ("database.host") is often a better answer than nesting objects you then have to defensively create at every level.
Common variants at a glance
| Pattern | Missing parent | Fix |
|---|---|---|
obj.a.b = x | obj.a | obj.a ??= {} before writing |
groups[key].push(x) | groups[key] | initialize the bucket first |
| direct nested state mutation | a state sub-object | rebuild with spreads, don't mutate |
| typo'd/mis-cased key | the wrong key entirely | match the real property name |
Debugging checklist
- ✓ The message names the property you tried to set — its parent is the undefined one
- ✓ Trace the path left-to-right and log each level to find the first undefined step
- ✓ Initialize that parent (
{}/[], or??=) immediately before the write - ✓ Grouping into a dynamic key? Initialize the bucket before pushing
- ✓ React/Redux state? Never mutate directly — rebuild with spreads
- ✓ Double-check key spelling and casing against the real object shape
Frequently Asked Questions
What does 'Cannot set properties of undefined (setting X)' mean?
You tried to write obj.a.b = value, but obj.a itself is undefined — there is no object there to set a property on. This is the write-side counterpart of "Cannot read properties of undefined": the same missing-parent problem, encountered while assigning instead of reading.
Why does building up a nested object fail like this?
Writing to a nested path requires every intermediate level to already be an object — result.stats.count = 1 needs result.stats to exist first. Unlike reading (which just gives undefined and lets you continue), writing to a property of undefined throws immediately, because there is nowhere to store the value.
How do I initialize a nested object safely?
Check and create each missing level before writing the final value: if (!obj.a) obj.a = {}; then obj.a.b = value. Or build the whole structure with a single object literal up front if you know its shape ahead of time, rather than assigning into it level by level.
Why does this happen when grouping array items into an object?
A common reduce pattern groups[item.category].push(item) fails the first time a new category key is seen, because groups[item.category] is still undefined. Initialize the bucket first: groups[item.category] = groups[item.category] || []; then push.
Why does this happen with React state?
Directly mutating nested state — state.user.settings.theme = 'dark' — both violates React's immutability expectations and throws this error if settings doesn't exist yet on the current state object. Use setState with a new object (spreading each level) instead of writing into the existing state tree.
How do I fix this quickly?
Find the parent in the write path that is undefined (the message doesn't say which — trace the chain manually or log each level), initialize it to an empty object or array before the write, or use a library/utility (like lodash's set, or optional chaining with nullish assignment obj.a ??= {}) to create intermediate levels safely.
References
- TypeError: "x" is (not) "y" (MDN)
- Optional chaining (?.) (MDN)
More JavaScript & runtime errors
Browse the full reference for JavaScript, Node.js, and database errors — exact message, cause, and fix.