jestjs/jest · error · Error
${methodName} is not declared writable or has no setter
Error message
${methodName} is not declared writable or has no setter What it means
Thrown by `SpyRegistry.spyOn` in spyRegistry.ts:121 when the property descriptor exists but is neither `writable` nor has a `set` accessor (`!(descriptor.writable || descriptor.set)`). jasmine2 replaces the value in place, so a frozen/read-only property without a setter cannot be overwritten.
Source
Thrown at packages/jest-jasmine2/src/jasmine/spyRegistry.ts:121
if (obj[methodName] && isSpy(obj[methodName])) {
if (this.respy) {
return obj[methodName];
} else {
throw new Error(
getErrorMsg(`${methodName} has already been spied upon`),
);
}
}
let descriptor;
try {
descriptor = Object.getOwnPropertyDescriptor(obj, methodName);
} catch {
// IE 8 doesn't support `definePropery` on non-DOM nodes
}
if (descriptor && !(descriptor.writable || descriptor.set)) {
throw new Error(
getErrorMsg(
`${methodName} is not declared writable or has no setter`,
),
);
}
const originalMethod = obj[methodName];
const spiedMethod = createSpy(methodName, originalMethod);
let restoreStrategy;
if (Object.prototype.hasOwnProperty.call(obj, methodName)) {
restoreStrategy = function () {
obj[methodName] = originalMethod;
};
} else {
restoreStrategy = function () {
if (!delete obj[methodName]) {
obj[methodName] = originalMethod;View on GitHub (pinned to f49721c78e)
Solutions
- If it is a getter/setter, use `spyOnProperty(obj, 'prop', 'get')` instead of `spyOn`.
- If the object is frozen, spy on the prototype or unfreeze/make the property writable before spying.
- Refactor the production code so the dependency is injectable rather than a hardcoded readonly property.
Example fix
// before spyOn(config, 'timeout') // timeout is a getter // after spyOnProperty(config, 'timeout', 'get').and.returnValue(5000)
Defensive patterns
Strategy: validation
Validate before calling
const d = Object.getOwnPropertyDescriptor(obj, methodName);
if (d && !d.writable && !d.set) { /* use spyOnProperty with access type, or refactor */ } Type guard
const isSpyable = (o: object, m: string): boolean => {
const d = Object.getOwnPropertyDescriptor(o, m);
return !d || Boolean(d.writable) || Boolean(d.set);
}; Prevention
- Use spyOnProperty for getters/setters instead of spyOn.
- Avoid freezing/sealing objects you intend to spy on.
When it happens
Trigger: Spying on a method declared `readonly` in a class, a property on a `Object.freeze`d object, a getter-only property without specifying the access type, or an inherited non-writable property.
Common situations: Trying to spy on a getter/setter with the two-arg `spyOn` instead of `spyOnProperty`; class with `readonly` TS fields compiled to non-writable descriptors; frozen config objects.
Related errors
- could not find an object to spy upon for ${methodName}()
- No method name supplied
- ${methodName}() method does not exist
- ${methodName} has already been spied upon
- ${propertyName} property does not exist
AI-assisted analysis of jestjs/jest@f49721c78e (2026-08-03).
Data as JSON: /data/errors/130caace7d3109a1.json.
Report an issue: GitHub.