vitejs/vite · error · Error

In virtual modules, all globs must start with '/'

Error message

In virtual modules, all globs must start with '/'

What it means

Thrown by `import.meta.glob` processing when the glob runs inside a virtual module (no on-disk directory / `dir` is falsy) and the glob is relative. Without a directory to resolve relative patterns against, Vite cannot expand them; you must pass absolute globs (starting with `/`) or a `base` option.

Source

Thrown at packages/vite/src/node/plugins/importMetaGlob.ts:457

  if (!matches.length) return null

  const s = new MagicString(code)

  const staticImports = (
    await Promise.all(
      matches.map(
        async ({
          globsResolved,
          isRelative,
          options,
          index,
          start,
          end,
          onlyKeys,
          onlyValues,
        }) => {
          if (!dir && !options.base && isRelative) {
            throw new Error("In virtual modules, all globs must start with '/'")
          }

          const cwd = getCommonBase(globsResolved) ?? root
          const files = (
            await glob(globsResolved, {
              absolute: true,
              cwd,
              dot: !!options.exhaustive,
              expandDirectories: false,
              caseSensitiveMatch: options.caseSensitive ?? true,
              ignore: options.exhaustive ? [] : ['**/node_modules/**'],
              extglob: false,
            })
          )
            .filter((file) => file !== id)
            .sort()

          const objectProps: string[] = []

View on GitHub (pinned to 89620f09af)

Solutions

  1. Use absolute globs starting with `/` rooted at the project root: `import.meta.glob('/src/pages/**/*.vue')`.
  2. Pass the `base` option so relative globs resolve: `import.meta.glob('./pages/**/*.vue', { base: '/src' })`.
  3. Move the `import.meta.glob` call into a real on-disk source file rather than a virtual module.

Example fix

// before — called inside a virtual module
const modules = import.meta.glob('./pages/**/*.vue');
// after
const modules = import.meta.glob('/src/pages/**/*.vue');
Defensive patterns

Strategy: validation

Validate before calling

function validateGlobsInVirtualModule(globs, options, isVirtual) {
  if (!isVirtual) return;
  for (const g of globs) {
    if (g.startsWith('/') || g.startsWith('!')) continue;
    if (g.startsWith('./') || g.startsWith('../')) {
      if (!options?.base) throw new Error(`Relative glob in virtual module needs base or leading '/': ${g}`);
    }
  }
}
// validateGlobsInVirtualModule(globs, options, id.startsWith('\0') || !id.includes('/'));

Type guard

function isVirtualModuleId(id) {
  return id.startsWith('\0') || !id.includes('/');
}

Prevention

When it happens

Trigger: In the glob transform, `if (!dir && !options.base && isRelative)` is true — i.e. `import.meta.glob('./foo/*.js')` is called from a virtual module id that has no associated directory.

Common situations: Calling `import.meta.glob` with relative patterns from within a virtual module generated by a plugin (e.g. a generated entry), or from code injected by a framework that lacks a real file path.

Related errors


AI-assisted analysis of vitejs/vite@89620f09af (2026-08-03). Data as JSON: /data/errors/9e7dc82f8fa20fe1.json. Report an issue: GitHub.