{"id":"cc69cef008b89427","repo":"jestjs/jest","slug":"cannot-use-spyon-on-a-primitive-value-typeofobj","errorCode":null,"errorMessage":"Cannot use spyOn on a primitive value; ${typeOfObject} given","messagePattern":"Cannot use spyOn on a primitive value; (.+?) given","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/jest-mock/src/index.ts","lineNumber":1280,"sourceCode":"  spyOn<\n    T extends object,\n    K extends ConstructorLikeKeys<T> | MethodLikeKeys<T>,\n    V extends Required<T>[K],\n  >(\n    object: T,\n    methodKey: K,\n  ): V extends ClassLike | FunctionLike ? Spied<V> : never;\n\n  spyOn<T extends object>(\n    object: T,\n    methodKey: keyof T,\n    accessType?: 'get' | 'set',\n  ): MockInstance {\n    if (\n      object == null ||\n      (typeof object !== 'object' && typeof object !== 'function')\n    ) {\n      throw new Error(\n        `Cannot use spyOn on a primitive value; ${this._typeOf(object)} given`,\n      );\n    }\n\n    if (methodKey == null) {\n      throw new Error('No property name supplied');\n    }\n\n    if (accessType) {\n      return this._spyOnProperty(object, methodKey, accessType);\n    }\n\n    const original = object[methodKey];\n\n    if (!original) {\n      throw new Error(\n        `Property \\`${String(\n          methodKey,","sourceCodeStart":1262,"sourceCodeEnd":1298,"githubUrl":"https://github.com/jestjs/jest/blob/f49721c78e195558b40913977c9230f5b7f559d8/packages/jest-mock/src/index.ts#L1262-L1298","documentation":"Thrown by jest.spyOn when the first argument is null/undefined or a primitive (string, number, bigint, boolean, symbol). The guard is object == null || (typeof object !== 'object' && typeof object !== 'function'). spyOn must install a property descriptor on the target, which is impossible on primitives — they are passed by value and have no extensible own slots.","triggerScenarios":"jest.spyOn(null, 'foo'); jest.spyOn(42, 'toString'); jest.spyOn('hello', 'length'); jest.spyOn(true, 'valueOf'); jest.spyOn(undefined, 'method'). Often happens when the object comes from a lookup that returned undefined (e.g. jest.spyOn(Modules.User, ...) where Modules.User is undefined).","commonSituations":"Mock not initialized before the test runs; module export renamed so the imported binding is undefined; destructuring at module top-level captured undefined; attempting to spy on a value-typed constant rather than the object that holds it.","solutions":["Log the object reference just before spyOn to confirm it is defined and is the right holder: console.log(target, typeof target).","Ensure the import resolves to the actual object instance (the module's exports), not a primitive re-exported from it.","If you must observe a primitive, wrap it: spy on the container object's property instead of the value.","For class/static methods, pass the class constructor (a function) rather than an instance or a primitive default."],"exampleFix":"// before\njest.spyOn(Config.timeout, 'apply'); // Config.timeout is 5000 (number)\n// after\njest.spyOn(Config, 'getTimeout'); // spy the method that returns it","handlingStrategy":"type-guard","validationCode":"if (target == null || (typeof target !== 'object' && typeof target !== 'function')) {\n  throw new Error(`spyOn target must be an object/function, got ${typeof target}`);\n}\njest.spyOn(target, 'method');","typeGuard":"const isSpyable = (v: unknown): v is object | ((...a: any[]) => any) =>\n  v != null && (typeof v === 'object' || typeof v === 'function');\n\nif (isSpyable(target)) {\n  jest.spyOn(target, 'method');\n}","tryCatchPattern":null,"preventionTips":["Log the imported binding before spying to confirm it is the object you expect.","Spy on the holder object, not a primitive value it owns.","For class methods, pass the class or an instance, never a default primitive."],"tags":["jest-mock","spyon","type-error","primitives"],"analyzedSha":"f49721c78e195558b40913977c9230f5b7f559d8","analyzedAt":"2026-08-03T20:16:28.571Z","schemaVersion":2}