vitejs/vite · error · Error

Failed to resolve ${JSON.stringify(id)}. This package is ESM

Error message

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.

What it means

Thrown during Vite's config-file bundling when a dependency is resolved as a CommonJS `require` but the package only ships ESM. The resolver first attempts `nodeResolveWithVite` with `isRequire: true`; on failure it retries without that flag, and if the import-style resolve succeeds it concludes the package is ESM-only and rejects the require. It points to the official troubleshooting guide because mixing CJS config files with ESM-only deps is a well-known interop failure.

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 89620f09af)

Solutions

  1. Rename the config to `vite.config.mjs` (or add `"type": "module"` to package.json) so it is treated as ESM and uses `import` instead of `require`.
  2. Replace any `require('pkg')` calls in the config with dynamic `import()` or static `import`.
  3. Downgrade the offending dependency to a version that still ships a CJS build, or find a CJS-compatible alternative.
  4. If you must keep CJS, pre-bundle the dep yourself or import it via a wrapper that re-exports from an ESM entry.

Example fix

// before (vite.config.cjs)
const { defineConfig } = require('vite')
const execa = require('execa') // execa v6+ is ESM-only

// after (rename to vite.config.mjs)
import { defineConfig } from 'vite'
import { execa } from 'execa'
Defensive patterns

Strategy: validation

Validate before calling

// Before writing a CJS config that requires a dep, verify it ships CJS:
const pkg = require.resolve('<dep>/package.json')
const meta = require(pkg)
const hasCjs = meta.main || (meta.exports && JSON.stringify(meta.exports).includes('require'))
if (!hasCjs && (meta.type === 'module' || meta.exports?.import)) {
  throw new Error('<dep> is ESM-only; convert this config to ESM (.mjs)')
}

Type guard

// Narrow a config format to whether require() is safe
function isCjsSafeConfig(configPath: string): boolean {
  return /\.c?js$/.test(configPath) && !fs.existsSync(path.join(process.cwd(), 'package.json'))
    ? false
    : require('./package.json').type !== 'module'
}

Prevention

When it happens

Trigger: A `vite.config.cjs` (or a project without `"type": "module"`) that `require()`s an ESM-only package, or any config whose bundled graph contains a `require(...)` of an ESM-only dep. The resolveId hook in config.ts:2577 runs with `isImport=false`, fails, then the retry at config.ts:2580 succeeds, triggering the throw at config.ts:2585.

Common situations: Using a newer ESM-only release of a library (e.g. `execa`, `got`, `node-fetch` v3+, `chalk` v5+) inside a legacy `.cjs` config; monorepo root lacking `"type": "module"`; scaffolding tools that emit CJS configs by default; upgrading a dependency whose major version moved to ESM-only.

Related errors


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