{"record":{"id":"c814e7fa17d9c3ac","repo":"vitest-dev/vitest","slug":"cannot-parse-url-because-module-striptypescr","errorCode":null,"errorMessage":"Cannot parse '${url}' because \"module.stripTypeScriptTypes\" is not supported. TypeScript coverage requires Node.js 22.15 or higher. This is NOT a bug of Vitest.","messagePattern":"Cannot parse '(.+?)' because \"module\\.stripTypeScriptTypes\" is not supported\\. TypeScript coverage requires Node\\.js 22\\.15 or higher\\. This is NOT a bug of Vitest\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/vitest/src/node/coverage.ts","lineNumber":743,"sourceCode":"\n  // TODO: should this be abstracted in `project`/`vitest` instead?\n  // if we decide to keep `viteModuleRunner: false`, we will need to abstract transformation in both main thread and tests\n  // custom --import=module.registerHooks need to be transformed as well somehow\n  async transformFile(url: string, project: TestProject, viteEnvironment: string, isTransformedByVite = true): Promise<TransformResult | null | undefined> {\n    const config = project.config\n\n    // vite is disabled, should transform manually if possible\n    if (config.experimental.viteModuleRunner === false || !isTransformedByVite) {\n      const pathname = url.split('?')[0]\n      const filename = pathname.startsWith('file://') ? fileURLToPath(pathname) : pathname\n      const extension = path.extname(filename)\n      const isTypeScript = extension === '.ts' || extension === '.mts' || extension === '.cts'\n      if (!isTypeScript) {\n        const code = await fs.readFile(filename, 'utf-8')\n        return { code, map: null }\n      }\n      if (!module.stripTypeScriptTypes) {\n        throw new Error(`Cannot parse '${url}' because \"module.stripTypeScriptTypes\" is not supported. TypeScript coverage requires Node.js 22.15 or higher. This is NOT a bug of Vitest.`)\n      }\n      const isTransform = process.execArgv.includes('--experimental-transform-types')\n        || config.execArgv.includes('--experimental-transform-types')\n        || process.env.NODE_OPTIONS?.includes('--experimental-transform-types')\n        || config.env?.NODE_OPTIONS?.includes('--experimental-transform-types')\n      const code = await fs.readFile(filename, 'utf-8')\n      return {\n        // `transform` mode will inject source maps comment at the end\n        code: module.stripTypeScriptTypes(code, { mode: isTransform ? 'transform' : 'strip' }),\n        map: null,\n      }\n    }\n\n    return project.vite.environments[viteEnvironment].transformRequest(url)\n  }\n\n  createUncoveredFileTransformer(ctx: Vitest) {\n    const projects = new Set([","sourceCodeStart":725,"sourceCodeEnd":761,"githubUrl":"https://github.com/vitest-dev/vitest/blob/1fa9837ec26533512fdcad8baebf249771bd340a/packages/vitest/src/node/coverage.ts#L725-L761","documentation":"When Vite's module runner is disabled for coverage transforms (experimental.viteModuleRunner === false, or the file wasn't transformed by Vite), Vitest falls back to Node's module.stripTypeScriptTypes to strip TS. That API exists only in Node 22.15+, so on older Node processing a .ts/.mts/.cts coverage target it throws — and the message explicitly states this is NOT a Vitest bug.","triggerScenarios":"Coverage over TypeScript files with experimental.viteModuleRunner === false (or non-Vite-transformed files) on a Node runtime below 22.15, so module.stripTypeScriptTypes is undefined.","commonSituations":"Older Node LTS (20.x or early 22); disabling viteModuleRunner for perf/isolation; running TS coverage without --experimental-transform-types.","solutions":["Upgrade Node to 22.15 or higher (module.stripTypeScriptTypes is available).","Enable the Vite module runner (remove experimental.viteModuleRunner: false).","Run Node with --experimental-transform-types (or set NODE_OPTIONS) so the transform mode is available.","Avoid TypeScript-only coverage in stripped mode on old Node."],"exampleFix":"// before\nnode --version # v20.x -> throws\nvitest run --coverage\n\n// after\nnvm use 22 # >= 22.15\nnode --experimental-transform-types ./node_modules/vitest/vitest.mjs run --coverage","handlingStrategy":"validation","validationCode":"import { gte } from 'semver'\nfunction nodeSupportsStripTs() {\n  return typeof require('module').stripTypeScriptTypes === 'function'\n    || gte(process.version, 'v22.15.0')\n}\n// before TS coverage with viteModuleRunner off: if (!nodeSupportsStripTs()) upgrade Node or enable runner","typeGuard":"function supportsStripTypeScriptTypes(): boolean {\n  return typeof (require('module').stripTypeScriptTypes) === 'function'\n}","tryCatchPattern":null,"preventionTips":["Pin Node >= 22.15 in CI and package.json engines when using TS coverage.","Don't disable experimental.viteModuleRunner unless Node supports stripTypeScriptTypes.","Add --experimental-transform-types to execArgv for transform-mode coverage."],"tags":["coverage","typescript","node-version","node-22"],"backgroundTag":null,"analyzedSha":"1fa9837ec26533512fdcad8baebf249771bd340a","analyzedAt":"2026-08-11T16:11:39.638Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}