oven-sh/bun · error · Error

Virtual CSS module not found: ${path}

Error message

Virtual CSS module not found: ${path}

What it means

The Bun Svelte plugin stashes CSS extracted from .svelte files in a virtualCssModules map keyed by path, then serves (and deletes) each entry the first time the bundler loads it in the 'bun-svelte' virtual namespace. If the bundler requests a virtual CSS path with no entry — already consumed, or registered by a different plugin instance — onLoad throws this error.

Source

Thrown at packages/bun-plugin-svelte/src/index.ts:129

          // NOTE: we assume js/ts modules won't have CSS blocks in them, so no
          // virtual modules get created.
          return {
            contents: result.js.code,
            loader: "js",
          };
        })
        .onResolve({ filter: /^bun-svelte:/ }, args => {
          return {
            path: args.path,
            namespace: "bun-svelte",
          };
        })
        .onLoad({ filter: /\.css$/, namespace: virtualNamespace }, args => {
          const { path } = args;

          const mod = virtualCssModules.get(path);
          if (!mod) throw new Error("Virtual CSS module not found: " + path);
          const { sourcePath, source } = mod;
          virtualCssModules.delete(path);

          return {
            contents: source,
            loader: "css",
            watchFiles: [sourcePath],
          };
        });
    },
  };
}

export default SveltePlugin({ development: true }) as BunPlugin;
export { SveltePlugin, type SvelteOptions };

View on GitHub (pinned to 8c5296ac45)

Solutions

  1. Create a fresh Bun.svelte() instance per build call instead of sharing one
  2. Run the concurrent builds sequentially if instances cannot be isolated
  3. Dedupe bun-plugin-svelte to a single copy (check overrides/lockfile)
  4. Do a clean rebuild to clear stale virtual path references

Example fix

// before
const plugin = Bun.svelte(); // one shared instance
await Promise.all([Bun.build({ plugins: [plugin] }), Bun.build({ plugins: [plugin] })]);

// after
await Promise.all([
  Bun.build({ plugins: [Bun.svelte()] }), // fresh instance per build
  Bun.build({ plugins: [Bun.svelte()] }),
]);
Defensive patterns

Strategy: try-catch

Validate before calling

const cssPlugin = Bun.svelte();
// do not share this instance across builds; instantiate per build call
const plugins = () => [Bun.svelte()];

Try / catch

try {
  await Bun.build({ entrypoints: [entry], plugins: [Bun.svelte()] });
} catch (err) {
  if (err instanceof Error && err.message.startsWith('Virtual CSS module not found')) {
    // stale/consumed virtual module: rebuild with a fresh plugin instance
    await Bun.build({ entrypoints: [entry], plugins: [Bun.svelte()] });
  } else throw err;
}

Prevention

When it happens

Trigger: Reusing one Bun.svelte() plugin instance across two concurrent builds (entries consumed by the first build are gone for the second); watch-mode rebuilds requesting a stale virtual path; two copies of the plugin (duplicate installs) splitting registrations and loads.

Common situations: Build scripts that hoist a single plugin instance and call Bun.build twice (e.g. client + server bundles in parallel); monorepos where bun-plugin-svelte appears twice in node_modules; HMR rebuilds racing the map deletion.

Related errors


AI-assisted analysis of oven-sh/bun@8c5296ac45 (2026-08-16). Data as JSON: /api/errors/e753bbb91da3a3ed. Report an issue: GitHub.