immutable-js/immutable-js · error · TypeError
Cannot update immutable value without .set() method: {}
Error message
Cannot update immutable value without .set() method: {} What it means
updateIn walks a keyPath level by level. At some level the existing value exists (wasNotSet is false) but is not a data structure — you cannot index into a number/string/boolean to continue the path. The message includes the path prefix that failed.
Source
Thrown at src/functional/set.ts:54
): C;
export function set<K, V, C extends Collection<K, V> | { [key: string]: V }>(
collection: C,
key: K | string,
value: V
): C {
if (isProtoKey(key)) {
return collection;
}
if (!isDataStructure(collection)) {
throw new TypeError(
'Cannot update non-data-structure value: ' + collection
);
}
if (isImmutable(collection)) {
// @ts-expect-error weird "set" here,
if (!collection.set) {
throw new TypeError(
'Cannot update immutable value without .set() method: ' + collection
);
}
// @ts-expect-error weird "set" here,
return collection.set(key, value);
}
// @ts-expect-error mix of key and string here. Probably need a more fine type here
if (hasOwnProperty.call(collection, key) && value === collection[key]) {
return collection;
}
const collectionCopy = shallowCopy(collection);
// @ts-expect-error mix of key and string here. Probably need a more fine type here
collectionCopy[key] = value;
return collectionCopy;
}
View on GitHub (pinned to 59fdaae676)
Solutions
- Fix the keyPath to stop at, or go around, the scalar value
- Ensure intermediate values are Maps/Objects: initialize defaults as Map()/{} not scalars
- Use updateIn's notSetValue argument or a custom updater that replaces non-structures: updateIn(keys, v => isDataStructure(v) ? v : Map(), then set)
Example fix
// before
Map({ user: { name: 'Ada' } }).updateIn(['user', 'name', 'first'], fn); // name is a string
// after
Map({ user: { name: { first: 'Ada' } } }).updateIn(['user', 'name', 'first'], fn); Defensive patterns
Strategy: type-guard
Validate before calling
import { isDataStructure } from 'immutable/dist/predicates';
// or: const isDS = v => isImmutable(v) || Array.isArray(v) || (v && typeof v === 'object');
function pathTraversable(state, keys) {
let cur = state;
for (const k of keys.slice(0, -1)) {
if (cur == null) return true; // will be created
if (!isDS(cur)) return false;
cur = cur.get ? cur.get(k) : cur[k];
}
return true;
} Type guard
const isDataStructure = (v: unknown): v is object => isImmutable(v as any) || Array.isArray(v) || (!!v && typeof v === 'object');
Try / catch
try { state.updateIn(path, fn); } catch (e) { if (/non-data-structure/.test(e.message)) { /* normalize leaf to Map and retry once */ } else throw e; } Prevention
- Initialize nested optional fields as Map()/{} instead of '' / 0 / false
- Keep keyPath arrays in one place, typed against the state schema
- Migrate persisted state to the current shape before updateIn
When it happens
Trigger: Map({ a: 5 }).updateIn(['a','b'], () => 1); state.updateIn(['user','name','first'], f) where user.name is a string; List path hitting a scalar element.
Common situations: State shape drift: a field that used to be a nested object became a scalar after a refactor or API change; key paths built dynamically with an extra segment; optional fields that default to '' or 0 instead of Maps.
Related errors
- Invalid merger function: {}
- Invalid keyPath: expected Ordered Collection or Array: {}
- Value type {} cannot be hashed.
- Expected [K, V] tuple: {}
- Can not call `Record` with an immutable Record as default va
AI-assisted analysis of immutable-js/immutable-js@59fdaae676 (2026-08-27).
Data as JSON: /api/errors/3d32ba0139f18ee5.
Report an issue: GitHub.