TypeError: Cannot set properties of undefined (setting 'x') when you assign a property to a value that is undefined. The three most common fixes: (1) initialize the parent object first — obj = obj ?? {}; obj.x = value; (2) add await if the value comes from an async function; (3) for nested paths, use obj.a ??= {}; obj.a.b = value. Note: optional chaining does not work on the left-hand side of assignment — that is a separate trap covered below.
What is TypeError: Cannot set properties of undefined?
This is a standard JavaScript TypeError thrown whenever you attempt to
write a property on a value that is undefined. Because
undefined is not an object, it cannot hold properties — any assignment to it
fails immediately at runtime.
It is the write-side complement to
TypeError: Cannot read properties of undefined.
The read variant triggers on obj.x (accessing a property); this variant triggers
on obj.x = value (assigning a property). Same root cause — the parent value is
undefined — but the direction of the operation differs.
Node.js ≥ 16:
TypeError: Cannot set properties of undefined (setting 'name')Node.js < 16:
TypeError: Cannot set property 'name' of undefined
Full Error Example
// app.js
const config = undefined;
config.port = 3000; // throws
TypeError: Cannot set properties of undefined (setting 'port')
at Object.<anonymous> (/project/app.js:2:14)
at Module._compile (node:internal/modules/cjs/loader:1356:14)
at Object.Module._extensions..js (node:internal/modules/cjs/loader:1414:10)
at Module.load (node:internal/modules/cjs/loader:1197:32)
at Function.Module._load (node:internal/modules/cjs/loader:1013:12)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/runMain:128:12)
at node:internal/main/run_main_module:28:49
The stack trace points to the exact file and line. The property name in parentheses —
here 'port' — tells you what you tried to write. Combined with the line number,
that is usually enough to identify the bug immediately.
Common Error Variants
TypeError: Cannot set properties of undefined (setting 'name')TypeError: Cannot set properties of undefined (setting 'id')TypeError: Cannot set properties of undefined (setting 'value')TypeError: Cannot set properties of undefined (setting 'current')TypeError: Cannot set properties of undefined (setting 'data')TypeError: Cannot set properties of undefined (setting 'status')TypeError: Cannot set properties of undefined (setting '0')TypeError: Cannot set property 'x' of undefined (Node.js < 16)
All Causes at a Glance
| Cause | Why it happens | Fix |
|---|---|---|
| Parent object not initialized | obj is undefined; you assign obj.x = v |
obj = obj ?? {}; obj.x = v |
| Nested path not initialized | obj.a is undefined when assigning obj.a.b = v |
obj.a ??= {}; obj.a.b = v |
| Array index out of bounds | arr[5] is undefined when array has fewer items |
Push/fill the array to the needed length first |
| Map/Set confusion | myMap is undefined, or wrong API used |
const myMap = new Map(); myMap.set('x', v) |
Missing await |
Variable holds a Promise, not the resolved object |
Add await before the async call |
Async function missing return |
Function implicitly returns undefined |
Add the missing return statement |
| Optional chaining on left-hand side | obj?.x = v is invalid or silent no-op |
Initialize obj explicitly; ??= for the parent |
| Redux / Zustand state mutation | State slice is undefined or state is frozen |
Return { ...state, x: v }; initialize slice in initial state |
| Frozen / sealed object (strict mode) | Object.freeze() prevents writes; throws in strict mode |
Remove freeze, or create a new object with the updated value |
| Missing function argument | Caller omits the object parameter; it arrives as undefined |
Add a default parameter: function f(obj = {}) { ... } |
Cause 1 – Parent Object Not Initialized
The most common root cause: the variable that should hold an object is still
undefined because it was never assigned, because a function did not return a
value, or because a conditional branch did not create the object.
// Wrong – obj was never assigned
let obj;
obj.port = 3000; // TypeError: Cannot set properties of undefined (setting 'port')
// Wrong – function missing return
function makeConfig() {
const cfg = { port: 3000 };
// forgot: return cfg;
}
const config = makeConfig(); // config is undefined
config.port = 8080; // TypeError: Cannot set properties of undefined (setting 'port')
// Fix – always return a value
function makeConfig() {
return { port: 3000 };
}
const config = makeConfig();
config.port = 8080; // works
// Fix – initialize with ?? before assigning
let obj;
obj = obj ?? {};
obj.port = 3000; // works
// Fix – provide an inline default
const config = makeConfig() ?? {};
config.port = 8080;
Cause 2 – Nested Path Not Initialized
Assigning to a deeply nested key — for example obj.a.b = value — requires
every intermediate object in the path to already exist. If obj.a is
undefined, the assignment to obj.a.b throws.
// Wrong – obj.db is undefined
const config = {};
config.db.host = 'localhost'; // TypeError: Cannot set properties of undefined (setting 'host')
// Fix 1 – initialize each intermediate level manually
config.db = config.db ?? {};
config.db.host = 'localhost';
// Fix 2 – nullish coalescing assignment (??=), Node.js 15+
config.db ??= {};
config.db.host = 'localhost';
// Fix 3 – initialize the full nested structure upfront
const config = {
db: {
host: 'localhost',
port: 5432,
},
};
// Fix 4 – lodash set() creates intermediate objects automatically
import set from 'lodash/set.js'; // ESM
// const { set } = require('lodash'); // CJS
set(config, 'db.host', 'localhost');
set(config, 'db.port', 5432);
??= pattern explained: obj.a ??= {} is shorthand
for obj.a = obj.a ?? {}. It assigns an empty object to obj.a only
when obj.a is null or undefined. Available since
Node.js 15 / V8 9.0.
Cause 3 – Array Index Out of Bounds
Reading a non-existent array index returns undefined. Assigning a property
to that undefined slot throws immediately.
const users = [{ name: 'Alice' }]; // length is 1
// Wrong – index 5 does not exist; users[5] is undefined
users[5].name = 'Bob'; // TypeError: Cannot set properties of undefined (setting 'name')
// Fix 1 – push to extend the array
users.push({ name: 'Bob' }); // index 1 now exists
// Fix 2 – check the index before assigning
if (users[5]) {
users[5].name = 'Bob';
} else {
users[5] = { name: 'Bob' }; // create the slot
}
// Fix 3 – pre-fill with Array.from for known sizes
const slots = Array.from({ length: 6 }, () => ({}));
slots[5].name = 'Bob'; // slots[5] is now {} and can receive properties
// Realistic async scenario – modifying a result set by index
const results = await db.query('SELECT * FROM users LIMIT 3');
// results[10] is undefined if the query returned fewer rows
if (results[10]) {
results[10].extra = true;
}
Cause 4 – Map / Set Confusion
A JavaScript Map is not a plain object. Properties must be stored with
myMap.set('key', value). If myMap itself is undefined
(never constructed with new Map()), calling myMap.set() or assigning
myMap.key = value both throw.
// Wrong – Map never initialized
let cache;
cache.set('user:1', { name: 'Alice' }); // TypeError: Cannot set properties of undefined (setting 'set')
// Also wrong – using object property syntax on a Map
const myMap = new Map();
myMap.user = 'Alice'; // does NOT store in the Map; adds a stray property on the Map object
// Correct – initialize with new Map() and use .set()
const cache = new Map();
cache.set('user:1', { name: 'Alice' });
console.log(cache.get('user:1')); // { name: 'Alice' }
// Same for Set – use .add(), not property assignment
const visited = new Set();
visited.add('/home');
console.log(visited.has('/home')); // true
// Module-level singleton pattern (common in Node.js services)
// cache.js
let _cache;
export function getCache() {
_cache ??= new Map(); // initialize once on first access
return _cache;
}
// In another file:
import { getCache } from './cache.js';
getCache().set('key', 'value'); // always safe
Cause 5 – Missing await (Async Resolves to Undefined)
Without await, an async function call returns a Promise object.
Assigning a property to something on the Promise — or to a property that
itself resolved to undefined — throws.
// Wrong – missing await; fetchUser() returns Promise, not a user object
async function updateUser() {
const user = fetchUser(); // Promise<{id: 1, name: 'Alice'}>
user.name = 'Bob'; // TypeError: Cannot set properties of undefined (setting 'name')
// (Promise has no .name that is undefined; actually Promise.name exists
// but it's read-only — you get a different error variant)
}
// More common variant: async function returns undefined (no return statement)
async function loadAndModify() {
const data = await fetchData();
const record = processData(data); // processData forgot to return a value
record.status = 'done'; // TypeError: Cannot set properties of undefined (setting 'status')
}
// Fix – add await and ensure functions return values
async function updateUser() {
const user = await fetchUser(); // now a real user object
user.name = 'Bob'; // works
// Or return a new object (safer, avoids mutation)
return { ...user, name: 'Bob' };
}
// Fix – verify the async result is defined before mutating
async function loadAndModify() {
const data = await fetchData();
const record = processData(data);
if (record == null) throw new Error('processData must return an object');
record.status = 'done';
}
Cause 6 – Optional Chaining Does NOT Work for Writes
This trips up many developers. Optional chaining (?.) is a read-only
operator. It cannot be placed on the left-hand side of an assignment expression.
// WRONG – SyntaxError or silent no-op depending on engine/mode
obj?.name = 'Alice'; // SyntaxError: Invalid left-hand side in assignment
// Also wrong – optional chaining on an intermediate doesn't help the write
const obj = undefined;
// obj?.nested.name = 'Alice'; // still throws when obj is undefined because
// the chain stops at obj? but the write still needs obj.nested
// Correct approach 1 – initialize parent with ??=
function ensureUser(container) {
container.user ??= {};
container.user.name = 'Alice'; // safe
}
// Correct approach 2 – explicit guard
function setName(obj, name) {
if (obj == null) throw new TypeError('obj must be an object');
obj.name = name;
}
// Correct approach 3 – create a new object (immutable-style)
const updated = { ...obj, name: 'Alice' }; // works even if obj is undefined? No —
// spread also throws on undefined; ensure obj is defined first:
const updated = { ...(obj ?? {}), name: 'Alice' };
?. only protects reads. For writes, you must
ensure the parent object exists before the assignment. Use ??=,
an explicit if check, or construct a new object with spread.
Cause 7 – Redux / Zustand State Mutation Antipattern
Redux and some other state management libraries freeze the state object in development
mode using Object.freeze(). Directly assigning to state properties throws
TypeError: Cannot assign to read only property (frozen) or
TypeError: Cannot set properties of undefined (when a state slice is
undefined because it was not initialized).
// Wrong – Redux reducer mutating state directly
function userReducer(state = { name: '' }, action) {
if (action.type === 'SET_NAME') {
state.name = action.payload; // TypeError in Redux dev mode (state is frozen)
return state;
}
return state;
}
// Wrong – accessing an undefined slice
function settingsReducer(state = {}, action) {
if (action.type === 'SET_THEME') {
state.preferences.theme = action.payload; // TypeError if state.preferences is undefined
}
return state;
}
// Correct – always return a NEW object
function userReducer(state = { name: '' }, action) {
if (action.type === 'SET_NAME') {
return { ...state, name: action.payload }; // new object, state unchanged
}
return state;
}
// Correct – initialize nested slices in the default parameter
function settingsReducer(state = { preferences: { theme: 'light' } }, action) {
if (action.type === 'SET_THEME') {
return {
...state,
preferences: { ...state.preferences, theme: action.payload },
};
}
return state;
}
// Zustand – correct usage of set()
import { create } from 'zustand';
const useStore = create((set) => ({
user: { name: '', role: '' },
setName: (name) => set((state) => ({
user: { ...state.user, name }, // never mutate state directly
})),
}));
Cause 8 – Strict Mode and Frozen / Sealed Objects
Object.freeze() prevents any property additions or modifications.
In strict mode (which Node.js ES Modules always run in), assigning to a frozen object
throws TypeError. In sloppy mode (CommonJS without 'use strict'),
the assignment silently fails — the value is never written.
// Object.freeze — assignment throws in strict mode
'use strict';
const CONFIG = Object.freeze({ port: 3000 });
CONFIG.port = 8080; // TypeError: Cannot assign to read only property 'port' of object
// Object.seal — can update existing properties, but cannot add new ones
'use strict';
const obj = Object.seal({ port: 3000 });
obj.port = 8080; // OK — existing property
obj.host = 'localhost'; // TypeError: Cannot add property host, object is not extensible
// Object.preventExtensions — same as seal for new properties
'use strict';
const obj2 = Object.preventExtensions({});
obj2.name = 'Alice'; // TypeError: Cannot add property name, object is not extensible
// ESM files are always in strict mode — the silent failure mode is unavailable
// In ESM:
const frozen = Object.freeze({ x: 1 });
frozen.x = 2; // TypeError (no 'use strict' needed — ESM implies it)
// Fix 1 – don't freeze config objects you need to mutate
const config = { port: 3000 };
config.port = 8080; // works
// Fix 2 – create a new object from a frozen one
const CONFIG = Object.freeze({ port: 3000 });
const overridden = { ...CONFIG, port: 8080 }; // works — spread creates a new unfrozen copy
// Fix 3 – check before assignment
function safeSet(obj, key, value) {
const desc = Object.getOwnPropertyDescriptor(obj, key);
if (desc && !desc.writable) {
throw new TypeError(`Property '${key}' is read-only on this object`);
}
obj[key] = value;
}
// How to detect if an object is frozen / sealed
console.log(Object.isFrozen(CONFIG)); // true
console.log(Object.isSealed(obj)); // true
Cause 9 – Missing Function Argument
When a caller omits a required object argument, the parameter arrives as
undefined, and any property assignment inside the function throws.
// Wrong – caller does not pass the required object
function setRole(user, role) {
user.role = role; // TypeError: Cannot set properties of undefined (setting 'role')
}
setRole(undefined, 'admin'); // passes undefined explicitly
setRole('admin'); // also wrong – 'admin' is in the user slot, role is undefined
// Fix 1 – validate at the function boundary
function setRole(user, role) {
if (user == null) throw new TypeError('setRole: user argument is required');
if (typeof role !== 'string') throw new TypeError('setRole: role must be a string');
user.role = role;
}
// Fix 2 – provide a default parameter
function setRole(user = {}, role = 'viewer') {
user.role = role;
return user;
}
// Fix 3 – TypeScript (catches at compile time)
function setRole(user: { role: string }, role: string): void {
user.role = role;
}
// TypeScript Error: Argument of type 'undefined' is not assignable to parameter of type '{ role: string }'
Safe Assignment Patterns
Nullish coalescing assignment (??=) for initialization
// Initialize an object property only if it is null or undefined
function ensureDefaults(config) {
config ??= {}; // ensure config itself exists
config.db ??= {}; // ensure nested db object exists
config.db.host ??= 'localhost';
config.db.port ??= 5432;
return config;
}
// Works safely even when called with undefined
const c = ensureDefaults(undefined);
console.log(c.db.host); // 'localhost'
Safe deep-set utility (no lodash dependency)
// CJS
function deepSet(obj, path, value) {
const keys = path.split('.');
let current = obj;
for (let i = 0; i < keys.length - 1; i++) {
const key = keys[i];
if (current[key] == null || typeof current[key] !== 'object') {
current[key] = {};
}
current = current[key];
}
current[keys[keys.length - 1]] = value;
return obj;
}
// Usage
const config = {};
deepSet(config, 'db.primary.host', 'localhost');
deepSet(config, 'db.primary.port', 5432);
console.log(config.db.primary.host); // 'localhost'
Immutable update pattern (spread, no mutation)
// Instead of mutating, return a new object
const original = { user: { name: 'Alice', role: 'viewer' } };
// Wrong – direct mutation (also breaks with frozen objects)
// original.user.role = 'admin';
// Correct – spread to create a new object at every level you change
const updated = {
...original,
user: {
...original.user,
role: 'admin',
},
};
console.log(original.user.role); // 'viewer' — unchanged
console.log(updated.user.role); // 'admin'
Validate before assign
// General guard pattern — use at function boundaries
function assignProperty(obj, key, value) {
if (obj == null) {
throw new TypeError(
`Cannot set property '${key}' on ${obj} — object is not initialized`
);
}
obj[key] = value;
}
// Async version — ensure the resolved value is defined
async function loadAndPatch(id) {
const record = await db.findById(id);
if (record == null) {
throw new Error(`Record ${id} not found`);
}
record.updatedAt = new Date().toISOString();
await db.save(record);
}
Debugging Checklist
- Read the stack trace — find the exact file and line where the property assignment fails.
- Note the property name in parentheses:
(setting 'x')tells you exactly what you tried to write. - Add
console.log()immediately before the failing line to inspect the target object. - Check whether the function populating the variable has a
returnstatement in every code path. - Check whether
awaitis missing before an async call — without it the variable is aPromise. - Check that all intermediate objects in a nested path are initialized (
obj.amust exist beforeobj.a.b = v). - Confirm you are not using
?.on the left-hand side of an assignment — it is read-only. - In Redux/Zustand: ensure reducers return new objects and that all state slices are initialized in the default state.
- Check for
Object.freeze(),Object.seal(), orObject.preventExtensions()on the target. - For arrays: log
arr.lengthand the index you are using before the assignment. - For Map/Set: confirm the collection was constructed with
new Map()ornew Set()and use.set()/.add().
??= {} everywhere without understanding
why the parent is undefined. If an object should always be initialized,
throw an explicit error at the function boundary so the bug surfaces early rather than
silently creating empty objects that hide the real data problem.
Frequently Asked Questions
What does TypeError: Cannot set properties of undefined mean?
It means you tried to assign a value to a property on something that is undefined. Because undefined is not an object, it cannot hold properties. The property name in parentheses — e.g. (setting 'port') — tells you what you tried to write. The stack trace line number tells you where in your code it happened. This is the write-side counterpart to Cannot read properties of undefined.
Does optional chaining (?.) work for property assignment?
No. Optional chaining is read-only. obj?.x = value is a SyntaxError in strict mode (which includes all ESM files) and silently does nothing in sloppy mode — the value is never written. To safely assign, ensure the parent object exists first: obj = obj ?? {}; obj.x = value or obj ??= {}; obj.x = value.
How do I safely assign to a deeply nested property?
Initialize every intermediate object in the path before assigning to the leaf. Use the ??= (nullish coalescing assignment) operator for conciseness:
config.db ??= {};
config.db.primary ??= {};
config.db.primary.host = 'localhost';
Or use lodash.set(config, 'db.primary.host', 'localhost') which creates all intermediate objects automatically.
Why does this error happen with async/await?
Two common async causes: (1) missing await — without it the variable holds a Promise object, not the resolved value; (2) the async function has no return statement and implicitly returns undefined. Fix by adding await and verifying every code path in your async function returns the expected object.
How do I fix this error in a Redux reducer?
Redux freezes state in development mode. Direct mutation (state.x = value) either throws TypeError: Cannot assign to read only property (frozen object) or TypeError: Cannot set properties of undefined (undefined slice). The fix: always return a new object — return { ...state, x: value } — and initialize all nested slices in the default state parameter: function reducer(state = { user: {}, settings: {} }, action) { ... }.
Why is this error different from "Cannot read properties of undefined"?
They share the same root cause — the parent value is undefined — but differ in direction. "Cannot read properties" fires when you access a property (obj.x). "Cannot set properties" fires when you assign a property (obj.x = value). The fixes are largely the same: initialize the parent object. See the companion page: Cannot read properties of undefined.
What is the old message for this error in Node.js before version 16?
In Node.js versions before 16, the error message was TypeError: Cannot set property 'x' of undefined. From Node.js 16 onwards, it became TypeError: Cannot set properties of undefined (setting 'x'). Both messages mean exactly the same thing and the fix is identical.
Why does assigning to arr[5].name throw when arr only has 3 elements?
Accessing an array index beyond its length returns undefined — there is no element there. Assigning a property to that undefined throws immediately. Fix by ensuring the element exists first: arr[5] = arr[5] ?? {}; arr[5].name = 'x';. Or push new elements to extend the array to the required length.
Why does this error occur in production but not locally?
Common reasons: (1) An environment variable is missing in production, so a config object is not populated. (2) An API returns a different shape in production (empty response, null nested field). (3) Local data always has the nested object present, but production data sometimes does not. (4) A race condition in async initialization is exposed by different timing in production. Fix: validate all external inputs and required env vars at startup and fail fast when they are absent.
Related Errors
TypeError: Cannot read properties of undefined (reading 'x')— the read-side companion: accessing a property onundefinedinstead of assigning oneUnhandledPromiseRejection— a rejected Promise with no.catch()or try/catch; often caused by the same missingawaitthat leads to this TypeErrorERR_INVALID_ARG_TYPE— Node.js core APIs throw this when you passundefinedwhere a specific type is requiredReferenceError: document is not defined— accessing browser globals (including DOM elements) in a Node.js environment; often precedes this TypeError