{"record":{"id":"c752f9aafdba360d","repo":"jestjs/jest","slug":"cannot-spy-on-the-string-propertykey-property","errorCode":null,"errorMessage":"Cannot spy on the ${String(propertyKey)} property because it is not a function; ${this._typeOf(original)} given instead.${typeof original === 'object' ? '' : ` If you are trying to mock a property, use `jest.replaceProperty(object, '${String(propertyKey)}', value)` instead.`}","messagePattern":"Cannot spy on the (.+?) property because it is not a function; (.+?) given instead\\.(.+?)', value\\)` instead\\.`\\}","errorType":"validation","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/jest-mock/src/index.ts","lineNumber":1411,"sourceCode":"    if (!descriptor.configurable) {\n      throw new Error(\n        `Property \\`${String(propertyKey)}\\` is not declared configurable`,\n      );\n    }\n\n    if (!descriptor[accessType]) {\n      throw new Error(\n        `Property \\`${String(\n          propertyKey,\n        )}\\` does not have access type ${accessType}`,\n      );\n    }\n\n    const original = descriptor[accessType];\n\n    if (!this.isMockFunction(original)) {\n      if (typeof original !== 'function') {\n        throw new TypeError(\n          `Cannot spy on the ${String(\n            propertyKey,\n          )} property because it is not a function; ${this._typeOf(\n            original,\n          )} given instead.${\n            typeof original === 'object'\n              ? ''\n              : ` If you are trying to mock a property, use \\`jest.replaceProperty(object, '${String(\n                  propertyKey,\n                )}', value)\\` instead.`\n          }`,\n        );\n      }\n\n      descriptor[accessType] = this._makeComponent({type: 'function'}, () => {\n        if (isPropertyOwner) {\n          // @ts-expect-error: mock is assignable\n          descriptor![accessType] = original;","sourceCodeStart":1393,"sourceCodeEnd":1429,"githubUrl":"https://github.com/jestjs/jest/blob/8e6d128e4a278059ecddecaa97400b04c8ae5fd9/packages/jest-mock/src/index.ts#L1393-L1429","documentation":"Thrown by _spyOnProperty (as a TypeError) when the located accessor is truthy but typeof !== 'function' (and not already a mock). Per the JS spec a descriptor's get/set are function or undefined, so reaching this branch implies a manually corrupted/tampered descriptor. It is a defensive guard; the message also suggests jest.replaceProperty for non-object originals.","triggerScenarios":"Manually assigning descriptor.get = 'notAFunction'; a custom Proxy returning a malformed descriptor; a third-party library mutating descriptors in a non-standard way.","commonSituations":"Extremely rare; only appears with non-standard descriptor manipulation or exotic Proxy traps. Normal code never hits this.","solutions":["Inspect Object.getOwnPropertyDescriptor(obj, key) and confirm get/set are functions.","Remove any code that mutates descriptors in a non-standard way.","Restore the property via Object.defineProperty with a proper function accessor before spying."],"exampleFix":"// before (descriptor tampered)\nObject.defineProperty(obj, 'x', {get: 42 as any, configurable: true});\njest.spyOn(obj, 'x', 'get');\n// after\nObject.defineProperty(obj, 'x', {get: () => 42, configurable: true});\njest.spyOn(obj, 'x', 'get');","handlingStrategy":"validation","validationCode":"const d = Object.getOwnPropertyDescriptor(obj, propertyKey);\nif (!d || typeof d[accessType] !== 'function') {\n  throw new Error(`${String(propertyKey)} ${accessType} accessor is not a function`);\n}\njest.spyOn(obj, propertyKey, accessType);","typeGuard":"const accessorIsFunction = (o: object, k: PropertyKey, a: 'get' | 'set'): boolean => {\n  const d = Object.getOwnPropertyDescriptor(o, k);\n  return !!d && typeof d[a] === 'function';\n};","tryCatchPattern":null,"preventionTips":["Never mutate descriptors to non-function values.","Audit any third-party code that redefines accessors.","If a Proxy is involved, log the descriptor it returns."],"tags":["spy","mock","accessor","edge-case"],"backgroundTag":null,"analyzedSha":"8e6d128e4a278059ecddecaa97400b04c8ae5fd9","analyzedAt":"2026-08-10T18:11:27.960Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}