{"id":"3b4ea898a8b6a941","repo":"jestjs/jest","slug":"primitives-cannot-leak-memory-you-passed-a-type","errorCode":null,"errorMessage":"Primitives cannot leak memory. You passed a ${typeof value}: <${prettyFormat(value)}>","messagePattern":"Primitives cannot leak memory\\. You passed a (.+?): <(.+?)>","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"packages/jest-leak-detector/src/index.ts","lineNumber":28,"sourceCode":"import {getHeapSnapshot, setFlagsFromString} from 'node:v8';\nimport {runInNewContext} from 'node:vm';\nimport {isPrimitive} from '@jest/get-type';\nimport {format as prettyFormat} from 'pretty-format';\n\ninterface LeakDetectorOptions {\n  shouldGenerateV8HeapSnapshot: boolean;\n}\n\nconst tick = promisify(setImmediate);\n\nexport default class LeakDetector {\n  private _isReferenceBeingHeld: boolean;\n  private _shouldGenerateV8HeapSnapshot: boolean;\n  private readonly _finalizationRegistry?: FinalizationRegistry<undefined>;\n\n  constructor(value: unknown, opt?: LeakDetectorOptions) {\n    if (isPrimitive(value)) {\n      throw new TypeError(\n        [\n          'Primitives cannot leak memory.',\n          `You passed a ${typeof value}: <${prettyFormat(value)}>`,\n        ].join(' '),\n      );\n    }\n\n    // When `_finalizationRegistry` is GCed the callback we set will no longer be called,\n    this._finalizationRegistry = new FinalizationRegistry(() => {\n      this._isReferenceBeingHeld = false;\n    });\n    this._finalizationRegistry.register(value as object, undefined);\n\n    this._isReferenceBeingHeld = true;\n\n    this._shouldGenerateV8HeapSnapshot =\n      opt?.shouldGenerateV8HeapSnapshot ?? true;\n","sourceCodeStart":10,"sourceCodeEnd":46,"githubUrl":"https://github.com/jestjs/jest/blob/f49721c78e195558b40913977c9230f5b7f559d8/packages/jest-leak-detector/src/index.ts#L10-L46","documentation":"Thrown by the `LeakDetector` constructor in jest-leak-detector/index.ts:28 when the value passed is a primitive (`isPrimitive(value)` true). Leak detection relies on a `FinalizationRegistry` weakly tracking an object; primitives are passed by value and cannot hold a strong reference, so tracking them is meaningless and the constructor rejects them with a TypeError.","triggerScenarios":"Constructing `new LeakDetector('foo')`, `new LeakDetector(42)`, `new LeakDetector(true)`, `new LeakDetector(null)`, `new LeakDetector(Symbol())`, or `new LeakDetector(0n)`. Most commonly hit indirectly when a test that uses `--detectLeaks` exports or returns a primitive as the module under test.","commonSituations":"Running Jest with `--detectOpenHandles`/`--detectLeaks` against a test module whose default export is a primitive (string/number); a leak-detector unit test that feeds a literal; wrapping a JSON config value.","solutions":["Pass an object/function/array instead of a primitive.","If the value is logically a primitive, skip leak detection for that test (it cannot leak by definition).","Wrap the primitive in an object only if you genuinely need to detect references to the wrapper."],"exampleFix":"// before\nnew LeakDetector('config-value')\n// after\nnew LeakDetector({ value: 'config-value' })","handlingStrategy":"type-guard","validationCode":"import {isPrimitive} from '@jest/get-type';\nif (isPrimitive(value)) { /* skip detection, primitives can't leak */ return; }\nnew LeakDetector(value);","typeGuard":"const isTrackable = (v: unknown): v is object =>\n  v != null && (typeof v === 'object' || typeof v === 'function');","tryCatchPattern":null,"preventionTips":["Only construct LeakDetector with objects, functions, or arrays.","In --detectLeaks test setups, ensure the module under test exports an object."],"tags":["leak-detector","primitive","finalization-registry","detect-leaks"],"analyzedSha":"f49721c78e195558b40913977c9230f5b7f559d8","analyzedAt":"2026-08-03T20:16:28.571Z","schemaVersion":2}