nodejs/node · critical · Error
The programmatic API was removed in npm v8.0.0
Error message
The programmatic API was removed in npm v8.0.0
What it means
npm v8+ removed its programmatic Node API. The entrypoint (deps/npm/index.js) only runs the CLI when invoked directly (require.main === module); requiring 'npm' from another module lands in the else branch and throws. The package is now CLI-only by design.
Source
Thrown at deps/npm/index.js:4
if (require.main === module) {
require('./lib/cli.js')(process)
} else {
throw new Error('The programmatic API was removed in npm v8.0.0')
}
View on GitHub (pinned to 1b2de5e052)
Solutions
- Spawn the CLI as a child process instead (e.g. execa('npm', ['install']) or child_process.spawn('npm', args, { stdio: 'inherit' }))
- For dependency-tree work use @npmcli/arborist; for package fetching use pacote; for registry access use npm-registry-fetch; for config use @npmcli/config
- If you only need npm's version, read require('npm/package.json').version (a static JSON file, not the entrypoint)
- For exact legacy behavior, pin an older npm (v7) in a dedicated environment
Example fix
// before
const npm = require('npm')
npm.load(() => npm.commands.install(['lodash']))
// after
const { spawn } = require('child_process')
spawn('npm', ['install', 'lodash'], { stdio: 'inherit' }) Defensive patterns
Strategy: validation
Validate before calling
// Detect before requiring: read the package metadata without invoking the entrypoint
const npmPkg = require('npm/package.json')
const major = Number(npmPkg.version.split('.')[0])
if (major >= 8) {
throw new Error(`npm@${npmPkg.version} has no programmatic API; spawn the CLI instead`)
} Type guard
// Node has no static type for this; guard at runtime on the resolved package version
function hasProgrammaticApi(npmVersion) {
const major = Number(String(npmVersion).split('.')[0])
return major < 8
} Prevention
- Treat the npm package as CLI-only: always spawn `npm` rather than require()ing it
- For in-process work, depend on the granular libraries (@npmcli/arborist, pacote, npm-registry-fetch, @npmcli/config)
- Pin your tooling against a known npm major version in CI
When it happens
Trigger: Calling require('npm') or import 'npm' from a Node script, build tool, or test harness. Anything that loads the package without executing it as the main module hits the else branch.
Common situations: Migrating tooling off npm v7 or earlier that drove npm in-process; scripts that did require('npm')({ argv: [...] }) to run commands; plugins that imported npm to read its version or config.
Related errors
- ${argv[2]} not recognized
- First argument `orgname` is required.
- Second argument `username` is required.
- Third argument `role` must be one of `owner`, `admin`, or `d
- Invalid package, must have name and version
AI-assisted analysis of nodejs/node@1b2de5e052 (2026-08-13).
Data as JSON: /api/errors/8338150a16527ee3.
Report an issue: GitHub.