The package you required ships only ES modules; require() can't load it. Your choices: migrate the file to ESM, downgrade the dependency, or use a dynamic import bridge.
Pure-ESM packages declare "type": "module" / "exports" with import-only paths. require() of them throws by design in modern Node.
npm install <pkg>@latest crossed the CJS→ESM boundary of that package. The lockfile pinning the old version is why CI was fine before.
npm install chalk@4 # or the equivalent CJS-compatible major for the offending package
# rename to .mjs (or set package.json "type": "module") and use: import chalk from 'chalk';
// const { default: chalk } = await import('chalk'); (inside an async function)
node -e "console.log(require('./node_modules/<pkg>/package.json').type || 'commonjs')"
Mixed repos: use .mjs/.cjs extensions per file instead of a blanket "type": "module" — explicit beats implicit during migration. Pure-ESM is a direction, not a fad: plan the migration on your schedule rather than one forced by an upgrade.
Webpack/esbuild/jest transform imports to a format require() can consume. Plain Node enforces the real module system. Test the production path (node directly), not just the dev path.
It's a legitimate stopgap: pin the last CJS major while you plan the ESM migration. Check the old major still receives security patches before parking on it long-term.
Our most-documented failures, packaged as ready-to-ship starter kits: Docker, Kubernetes, and Terraform.
Browse the template store →One-time. Yours to modify. Instant download from the NinjaOps template store.