vitejs/vite · error · Error

Failed to resolve ${id}. This package is ESM only but it was

Error message

Failed to resolve ${id}. This package is ESM only but it was tried to load by `require`. See https://vite.dev/guide/troubleshooting.html#this-package-is-esm-only for more details.

What it means

During config bundling Vite resolves import specifiers with nodeResolveWithVite. When a specifier resolves as ESM-only (require fails) but works as a static import, Vite throws a targeted error pointing at the troubleshooting guide. This typically means an ESM-only package is being pulled into a CJS context, or a CJS-resolver path was forced.

Source

Thrown at packages/vite/src/node/config.ts:2585

            }

            const isImport = isESM || kind === 'dynamic-import'
            let idFsPath: string | undefined
            try {
              idFsPath = nodeResolveWithVite(id, importer, {
                root,
                isRequire: !isImport,
              })
            } catch (e) {
              if (!isImport) {
                let canResolveWithImport = false
                try {
                  canResolveWithImport = !!nodeResolveWithVite(id, importer, {
                    root,
                  })
                } catch {}
                if (canResolveWithImport) {
                  throw new Error(
                    `Failed to resolve ${JSON.stringify(
                      id,
                    )}. This package is ESM only but it was tried to load by \`require\`. See https://vite.dev/guide/troubleshooting.html#this-package-is-esm-only for more details.`,
                  )
                }
              }
              throw e
            }
            if (!idFsPath) return
            // always no-externalize json files as rolldown does not support import attributes
            if (idFsPath.endsWith('.json')) {
              return idFsPath
            }

            if (idFsPath && isImport) {
              idFsPath = pathToFileURL(idFsPath).href
            }
            return { id: idFsPath, external: true }

View on GitHub (pinned to b4d66fee14)

Solutions

  1. Rename your config to vite.config.mjs (or set package.json type: module) so it is loaded as ESM.
  2. Downgrade or replace the ESM-only package with a CJS-compatible version.
  3. Bundle the dependency into the config (configLoader: 'bundle') rather than requiring it at runtime.

Example fix

// before: vite.config.js (CommonJS) requires an ESM-only package
const pkg = require('esm-only-pkg')
// after: rename to vite.config.mjs and import
import pkg from 'esm-only-pkg'
Defensive patterns

Strategy: validation

Validate before calling

function loadConfigAsEsm(specifier) {
  try { return require.resolve(specifier); }
  catch (e) {
    if (/^Error\(ERR_REQUIRE_ESM\)/.test(String(e.message)) || e.code === 'ERR_REQUIRE_ESM') {
      throw new Error(`${specifier} is ESM-only; load the config as ESM (vite.config.mjs)`);
    }
    throw e;
  }
}

Type guard

function looksEsmOnly(pkg) {
  return Boolean(pkg && pkg.type === 'module' && !pkg.main);
}

Prevention

When it happens

Trigger: An ESM-only dependency (no CJS entry) is required via require() in a config or plugin loaded in CJS mode; or the resolver falls back to the require path and the package lacks main/require fields.

Common situations: Mixing ESM-only packages (e.g. 'chalk', 'execa' v6+) into a CommonJS vite.config.js; older Node versions whose default resolution prefers require; packages whose package.json lacks a valid exports map.

Related errors


AI-assisted analysis of vitejs/vite@b4d66fee14 (2026-08-11). Data as JSON: /api/errors/a69aba04e06fe318. Report an issue: GitHub.