{"record":{"id":"47cddda507bbe05e","repo":"jestjs/jest","slug":"propertyname-is-not-declared-configurable","errorCode":null,"errorMessage":"${propertyName} is not declared configurable","messagePattern":"(.+?) is not declared configurable","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/jest-jasmine2/src/jasmine/spyRegistry.ts","lineNumber":178,"sourceCode":"      }\n\n      if (!propertyName) {\n        throw new Error(getErrorMsg('No property name supplied'));\n      }\n\n      let descriptor: PropertyDescriptor | undefined;\n      try {\n        descriptor = Object.getOwnPropertyDescriptor(obj, propertyName);\n      } catch {\n        // IE 8 doesn't support `definePropery` on non-DOM nodes\n      }\n\n      if (!descriptor) {\n        throw new Error(getErrorMsg(`${propertyName} property does not exist`));\n      }\n\n      if (!descriptor.configurable) {\n        throw new Error(\n          getErrorMsg(`${propertyName} is not declared configurable`),\n        );\n      }\n\n      if (!descriptor[accessType]) {\n        throw new Error(\n          getErrorMsg(\n            `Property ${propertyName} does not have access type ${accessType}`,\n          ),\n        );\n      }\n\n      if (obj[propertyName] && isSpy(obj[propertyName])) {\n        if (this.respy) {\n          return obj[propertyName];\n        } else {\n          throw new Error(\n            getErrorMsg(`${propertyName} has already been spied upon`),","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/jestjs/jest/blob/8e6d128e4a278059ecddecaa97400b04c8ae5fd9/packages/jest-jasmine2/src/jasmine/spyRegistry.ts#L160-L196","documentation":"Thrown by SpyRegistry._spyOnProperty when the property descriptor's configurable flag is false (spyRegistry.ts:177-181). Object.defineProperty can only redefine a property whose descriptor is configurable; a non-configurable property cannot be replaced with a spy.","triggerScenarios":"spyOn on a property defined via Object.defineProperty(obj, 'p', { configurable: false }), built-in prototype properties like Function.prototype.arguments, frozen object accessor properties, or sealed objects.","commonSituations":"Spying on built-ins (window.location, process.env accessors, native prototype members); library authors who deliberately lock properties; Object.seal/Object.freeze on the target; spying on class private fields compiled to non-configurable descriptors.","solutions":["Redefine the property as configurable before spying: Object.defineProperty(obj, 'p', { configurable: true, ...rest }).","Spy on a wrapper module you control rather than the locked native object.","Use jest.mock to replace the entire module instead of spying on a single locked property.","If testing env accessors, copy needed values to a plain object and spy on that."],"exampleFix":"// before\nspyOn(globalThis, 'location', 'get'); // non-configurable\n// after\njest.mock('./env', () => ({getLocation: () => '/fake'}));","handlingStrategy":"validation","validationCode":"const d = Object.getOwnPropertyDescriptor(obj, propertyName);\nif (d && !d.configurable) {\n  throw new Error(`${String(propertyName)} is non-configurable; mock the module instead`);\n}\nspyOn(obj, propertyName, 'get');","typeGuard":"const isConfigurable = (o: object, k: PropertyKey): boolean => {\n  const d = Object.getOwnPropertyDescriptor(o, k);\n  return !!d && !!d.configurable;\n};","tryCatchPattern":null,"preventionTips":["Do not spy on native non-configurable accessors; mock the module instead.","Redefine locked properties as configurable before spying.","Spy on wrapper modules you control."],"tags":["jest","spyon","mocking","property-accessor","non-configurable","frozen","native"],"backgroundTag":null,"analyzedSha":"8e6d128e4a278059ecddecaa97400b04c8ae5fd9","analyzedAt":"2026-08-10T18:11:27.960Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}