{"id":"a0a79e60c806d938","repo":"vitejs/vite","slug":"module-url-was-mistakenly-invalidated-during","errorCode":null,"errorMessage":"Module \"${url}\" was mistakenly invalidated during fetch phase.","messagePattern":"Module \"(.+?)\" was mistakenly invalidated during fetch phase\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"packages/vite/src/module-runner/runner.ts","lineNumber":298,"sourceCode":"    const isCached = !!(typeof cachedModule === 'object' && cachedModule.meta)\n\n    const fetchedModule = // fast return for established externalized pattern\n      (\n        url.startsWith('data:') || this.isBuiltin?.(url)\n          ? { externalize: url, type: 'builtin' }\n          : await this.transport.invoke('fetchModule', [\n              url,\n              importer,\n              {\n                cached: isCached,\n                startOffset: this.evaluator.startOffset,\n              },\n            ])\n      ) as ResolvedResult\n\n    if ('cache' in fetchedModule) {\n      if (!cachedModule || !cachedModule.meta) {\n        throw new Error(\n          `Module \"${url}\" was mistakenly invalidated during fetch phase.`,\n        )\n      }\n      return cachedModule\n    }\n\n    const moduleId =\n      'externalize' in fetchedModule\n        ? fetchedModule.externalize\n        : fetchedModule.id\n    const moduleUrl = 'url' in fetchedModule ? fetchedModule.url : url\n    const module = this.evaluatedModules.ensureModule(moduleId, moduleUrl)\n\n    if ('invalidate' in fetchedModule && fetchedModule.invalidate) {\n      this.evaluatedModules.invalidateModule(module)\n    }\n\n    fetchedModule.url = moduleUrl","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/vitejs/vite/blob/89620f09afcfef6b35e7bb8660132ab5b4d0cd3b/packages/vite/src/module-runner/runner.ts#L280-L316","documentation":"Thrown in getModuleInformation when the server's fetchModule response includes a cache: true flag (indicating the module hasn't changed since last fetch) but the runner has no cached module for this URL (cachedModule is null or has no meta). This is a state inconsistency: the server assumes the client has the module cached, but the client's cache was cleared or never populated. It typically indicates a race condition between invalidation and fetching.","triggerScenarios":"The runner's evaluatedModules cache is cleared (via clearCache()) while a concurrent fetch is in-flight. The server then responds with cache: true for a module the runner no longer has. Can also occur with custom transport implementations that incorrectly cache or replay fetch responses, or when multiple runner instances share a server that assumes state continuity.","commonSituations":"Calling clearCache() during HMR or SSR while async imports are pending. Race conditions in test environments where the runner is reset between tests but async operations from the previous test are still running. Custom transport middleware that incorrectly marks responses as cacheable.","solutions":["Avoid calling clearCache() while imports are in-flight; wait for all pending import() promises to settle first.","If using HMR, let the HMR handler manage cache invalidation rather than calling clearCache() manually.","Ensure your transport implementation correctly forwards the cache flag and doesn't falsely set it.","In test environments, fully tear down the runner (await close()) before creating a new one rather than clearing mid-operation."],"exampleFix":"// before — clearing cache during in-flight imports\nrunner.import('/src/a.ts') // in-flight\nrunner.clearCache()        // wipes cache\n// the server may now respond with { cache: true } for a module\n//   the runner no longer has -> throws\n// after — await pending imports before clearing\nawait pendingImportPromise\nrunner.clearCache()","handlingStrategy":"validation","validationCode":"// Prevent concurrent clearCache + import races\nlet importCount = 0\nlet clearPending = false\n\nasync function guardedImport(runner, url) {\n  if (clearPending) throw new Error('Cache clear in progress')\n  importCount++\n  try {\n    return await runner.import(url)\n  } finally {\n    importCount--\n  }\n}\n\nasync function guardedClearCache(runner) {\n  if (importCount > 0) {\n    console.warn('Waiting for in-flight imports before clearing cache')\n    // use a promise that resolves when importCount reaches 0\n  }\n  clearPending = true\n  runner.clearCache()\n  clearPending = false\n}","typeGuard":null,"tryCatchPattern":"try {\n  await runner.import(url)\n} catch (e) {\n  if (e.message.includes('mistakenly invalidated during fetch phase')) {\n    console.error('Cache race detected. Avoid calling clearCache() during imports.')\n    // may need to recreate the runner\n  }\n  throw e\n}","preventionTips":["Never call clearCache() while import() promises are unresolved.","Implement a mutex or counter to serialize cache clears with imports.","Let the HMR handler manage invalidation rather than manual clearCache().","In tests, fully close() and recreate the runner between test suites."],"tags":["module-runner","race-condition","cache","ssr","hmr"],"analyzedSha":"89620f09afcfef6b35e7bb8660132ab5b4d0cd3b","analyzedAt":"2026-08-03T19:28:02.920Z","schemaVersion":2}