neoclide/coc.nvim · error · Error
ERR_REQUIRE_ESM
ERR_REQUIRE_ESM
Error message
require() of ES Module ${url} not supported. Use dynamic import() instead. What it means
Extension require() refuses to load an ES module via CommonJS require. When module resolution finds a file whose package.json declares "type": "module" (an ESM .js or .mjs), the loader throws ERR_REQUIRE_ESM telling the extension author to use dynamic import().
Source
Thrown at src/extension/loader.ts:241
/**
* Node-compatible `require.resolve.paths` for a request from a parent
* module. Returns null for builtins, like Node does.
*/
public resolvePaths(request: string, parent: ExtensionCommonJSModule): string[] | null {
return Module._resolveLookupPaths(request, this.parentModule(parent))
}
/**
* Extension-local require: API injection, builtins, then modules in this
* runtime.
*/
public require(request: string, parent: ExtensionCommonJSModule): unknown {
if (request === 'coc.nvim') return this.runtime.api
if (this.isBuiltin(request)) return this.loadBuiltin(request)
let resolved = resolveExtensionModule(this.runtime, request, parent.filename, 'require')
if (resolved.type === 'file' && resolved.format === 'module') {
throw requireESMError(resolved.filename)
}
// Builtins and coc.nvim are handled above, so resolution always yields a
// file module here.
return this.load((resolved as any).filename, parent)
}
public load(filename: string, parent?: ExtensionCommonJSModule, isMain = false): unknown {
const cacheKey = this.normalizeFilename(filename)
const ext = path.extname(cacheKey).toLowerCase()
if (ext === '.json') return this.loadJson(cacheKey, parent)
if (ext === '.node') return this.loadNative(cacheKey, parent)
if (ext === '.js' || ext === '.cjs' || ext === '') return this.loadJavaScript(cacheKey, parent)
if (ext === '.mjs') throw requireESMError(cacheKey)
throw new Error(`Unsupported module type "${ext}" for ${cacheKey}`)
}
/**
* Load a native addon outside the VM. The addon is dlopen'd by Node and itsView on GitHub (pinned to 50e974d969)
Solutions
- Use await import('some-pkg') instead of require in the extension code.
- Pin/downgrade the dependency to the last CommonJS-compatible version.
- Switch the extension itself to ESM if the loader configuration supports it.
- Use a dual-format build (e.g. tsup/unbuild) so a CJS entry exists to require.
Example fix
// before
const chalk = require('chalk')
// after
const { default: chalk } = await import('chalk') Defensive patterns
Strategy: fallback
Validate before calling
const pkg = JSON.parse(fs.readFileSync(require.resolve('some-pkg/package.json'), 'utf8'))
if (pkg.type === 'module') throw new Error('ESM-only dependency; use import()') Try / catch
let mod
try { mod = require('some-pkg') } catch (e) { if (e.code === 'ERR_REQUIRE_ESM') mod = await import('some-pkg').then(m => m.default ?? m); else throw e } Prevention
- Check package.json "type" of new dependencies before requiring them
- Pin CJS-compatible versions of popular ESM-only packages (chalk@4, got@11, node-fetch@2)
- Prefer dynamic import() for anything possibly ESM
- Keep extensions building dual CJS/ESM output
When it happens
Trigger: Extension code calls require('some-pkg') where the resolved package's package.json has "type": "module", or requires a .mjs file directly.
Common situations: A dependency upgraded to an ESM-only major version (e.g. chalk 5+, got 12+, node-fetch 3+) while extension code still uses require(); loading a .mjs file; mixing require of a dual package from a CommonJS extension.
Related errors
- coc.nvim requires Node.js VM modules support for ESM extensi
- ESM import of native addon ${resolved.filename} is not suppo
AI-assisted analysis of neoclide/coc.nvim@50e974d969 (2026-08-31).
Data as JSON: /api/errors/31ea8b686f28554b.
Report an issue: GitHub.