{"id":"45f4e7e7d1a45f6e","repo":"vitejs/vite","slug":"circular-worker-imports-detected-vite-does-not-su","errorCode":null,"errorMessage":"Circular worker imports detected. Vite does not support it. Import chain: ${newBundleChain.map((id) => prettifyUrl(id, config.root)).join(' -> ')}","messagePattern":"Circular worker imports detected\\. Vite does not support it\\. Import chain: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/vite/src/node/plugins/worker.ts","lineNumber":178,"sourceCode":"const workerOutputCaches = new WeakMap<ResolvedConfig, WorkerOutputCache>()\n\nasync function bundleWorkerEntry(\n  config: ResolvedConfig,\n  id: string,\n): Promise<WorkerBundle> {\n  const input = cleanUrl(id)\n\n  const workerOutput = workerOutputCaches.get(config.mainConfig || config)!\n  workerOutput.removeBundleIfInvalidated(input)\n\n  const bundleInfo = workerOutput.getWorkerBundle(input)\n  if (bundleInfo) {\n    return bundleInfo\n  }\n\n  const newBundleChain = [...config.bundleChain, input]\n  if (config.bundleChain.includes(input)) {\n    throw new Error(\n      'Circular worker imports detected. Vite does not support it. ' +\n        `Import chain: ${newBundleChain.map((id) => prettifyUrl(id, config.root)).join(' -> ')}`,\n    )\n  }\n\n  // bundle the file as entry to support imports\n  const { rolldown } = await import('rolldown')\n  const { plugins, rolldownOptions, format } = config.worker\n  const workerConfig = await plugins(newBundleChain)\n  const workerEnvironment = new BuildEnvironment('client', workerConfig) // TODO: should this be 'worker'?\n  await workerEnvironment.init()\n\n  const chunkMetadataMap = new ChunkMetadataMap()\n  const workerBuildTarget = workerEnvironment.config.build.target\n  const bundle = await rolldown({\n    ...rolldownOptions,\n    input,\n    plugins: workerEnvironment.plugins.map((p) =>","sourceCodeStart":160,"sourceCodeEnd":196,"githubUrl":"https://github.com/vitejs/vite/blob/89620f09afcfef6b35e7bb8660132ab5b4d0cd3b/packages/vite/src/node/plugins/worker.ts#L160-L196","documentation":"When Vite bundles a worker entry it tracks config.bundleChain; bundleWorkerEntry at packages/vite/src/node/plugins/worker.ts:178 appends the current input and, if that input already appears in the chain, throws with the full import cycle. Vite cannot emit a worker that transitively imports itself because the bundle has no fixed point.","triggerScenarios":"Worker module A imports worker module B which imports A; a worker entry re-exports from a barrel (index.ts) that re-exports the worker itself; new Worker(new URL('./w.js', import.meta.url)) where w.js dynamically imports a module that constructs the same worker.","commonSituations":"Refactoring that introduced a barrel file circularly referencing a worker; shared utility that, through imports, pulls the worker entry back in; monorepo workspace cycles where a worker and a shared package import each other.","solutions":["Break the cycle: extract the shared code that both worker modules need into a third, dependency-free module and import that from both.","Inspect the printed Import chain to find the offending edge and remove or invert that one import.","If the cycle is via a barrel (index.ts), import the specific file directly instead of the barrel to avoid re-pulling the worker.","Lazy-load the back-edge with a dynamic import() inside a function so it is no longer a static module-graph cycle."],"exampleFix":"// before: worker.js -> utils.js -> worker.js  (cycle)\n// worker.js\nimport { x } from './utils'\n// utils.js\nimport('./worker')\n\n// after: extract shared.js with no worker import\n// worker.js -> shared.js <- utils.js","handlingStrategy":"validation","validationCode":"// Detect a static cycle in worker imports before bundling using a simple DFS.\nfunction hasWorkerCycle(graph: Record<string, string[]>, entry: string): boolean {\n  const seen = new Set<string>()\n  const dfs = (n: string, path: Set<string>): boolean => {\n    if (path.has(n)) return true\n    if (seen.has(n)) return false\n    seen.add(n); path.add(n)\n    return (graph[n] ?? []).some(c => dfs(c, path))\n  }\n  return dfs(entry, new Set())\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep worker modules leaf-like: they should not import other worker entries.","Avoid barrel files (index.ts) in the worker's import path to prevent accidental cycles.","Run a static cycle check (madge, dependency-cruiser) over worker entry graphs in CI."],"tags":["worker","circular-import","bundle","dependency-graph"],"analyzedSha":"89620f09afcfef6b35e7bb8660132ab5b4d0cd3b","analyzedAt":"2026-08-03T19:28:02.920Z","schemaVersion":2}