This message is a fossil from an older JavaScript runtime: SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode. If it appears while installing a modern npm package, the package is usually not the surprising part—the node executable that parsed it is.
Why this error happened
let, const, and class are standard JavaScript. Early Node.js releases shipped V8 versions that exposed some block-scoped syntax only under strict-mode or feature constraints. A newer dependency could therefore fail during parsing—before its code executed at all. Every Node.js line old enough to produce the original error is now end-of-life.
1. Identify the runtime that produced the stack trace
node --version
npm --version
command -v node
node -p "process.execPath"
node -p "process.versions"What these checks establish
node --versionidentifies the runtime major; compare it with the project’s supported range and Node.js release-status page.npm --versionrecords the package-manager version but does not prove npm owns the Node executable.command -v nodeshows the shell resolution, whileprocess.execPathreports the executable from inside the running process.process.versionsrecords the embedded V8 and other component versions—valuable evidence when an IDE, CI runner, or service behaves differently from your terminal.
2. Check what the project actually requires
node -p "require('./package.json').engines || {}"
npm config get prefix
npm config get userconfigDo not upgrade by guesswork
A
package.jsonengines.noderange communicates the versions the project or package expects; CI and deployment configuration may narrow it further.No
enginesentry is not proof that every Node release works. Read the project’s support policy and lockfile history.The npm prefix and user-config path expose mixed root/user installations and unexpected global state.
Select a maintained Node.js line that satisfies the application and its dependencies. Production guidance favors Active or Maintenance LTS, not simply the numerically newest release.
3. Switch to a supported runtime with a version manager
Use an installation method listed on the official Node.js download page or one approved by your organization. For a shell already configured with nvm, the following selects the current LTS line without hard-coding a version that will age.
nvm install --lts
nvm use --lts
node --version
node -p "process.execPath"Risk level: caution. Review the command before running it.
What changes—and what does not
nvm install --ltsdownloads the current LTS release through the configured nvm installation; review nvm’s provenance and organizational policy first.nvm use --ltschanges the active runtime for the current shell. New shells need the project’s version/default configuration.The final checks prove the new runtime is selected instead of an older
/usr/bin/nodeor/usr/local/bin/node.A runtime switch does not automatically rebuild native add-ons or make an unsupported application compatible; continue with the project’s tests.
4. Reinstall project dependencies predictably
npm ci
npm testRisk level: caution. Review the command before running it.
Why a clean dependency install matters
npm cirequires a compatible lockfile, removes the existingnode_modules, and installs the locked dependency graph without rewriting it.Because it deletes the dependency directory, preserve any illicit manual edits before running it—then replace those edits with a patch or declared dependency.
Native add-ons may need rebuilding for the new Node ABI; a clean install handles their normal lifecycle scripts.
npm testis only as meaningful as the project’s configured suite. Also run build, lint, integration, and smoke checks required by the repository.
5. Prove block-scoped syntax parses
node -e "{ const answer = 42; let label = 'ready'; console.log(label, answer); }"What this tiny test isolates
node -easks the selected runtime to parse and execute the supplied script without involving npm or the application.constandletare scoped to the braces; a supported modern runtime accepts them in ordinary CommonJS scripts.Successful output proves syntax support, not application compatibility. If the real command still fails, compare executable paths and reproduce through the same service, IDE, or CI environment.
If this command fails on a purportedly modern release, the version/path evidence is inconsistent; resolve that before changing source code.
Strict mode is not the modern fix
Adding "use strict" could alter parsing behavior in some ancient engines, which explains the wording of the historical error. It is not a responsible fix today: those runtimes lack years of security patches and ecosystem compatibility. Modern ECMAScript modules are strict by definition, while modern CommonJS code also supports block-scoped declarations without a strict-mode directive.
Do not confuse this with CommonJS/ESM errors
Cannot use import statement outside a moduleconcerns module format, not support forletorconst.Node recognizes ESM through markers such as
.mjsor a nearestpackage.jsonwith"type": "module";.cjsexplicitly selects CommonJS.Converting formats can affect
require,exports,module.exports,__filename, resolution, and test/build tooling. Do not add"type": "module"only to silence one message.Match the dependency’s documented import style and the project’s chosen module system, then test the entire dependency graph.
When the terminal works but the build still fails
CI uses an old image: print
node --versionandprocess.execPathinside the failing job, then update the runtime action/image deliberately.IDE task uses another PATH: restart the IDE and configure its runtime explicitly. Integrated terminals and task runners can resolve differently.
systemd/cron uses system Node: inspect the service unit or scheduled environment; interactive version-manager initialization is usually absent.
`sudo npm` finds another Node: stop mixing root and user package trees. Install dependencies within the project as its owning user.
Docker image is stale: pin a maintained official base-image line, rebuild without stale cache where appropriate, and test native dependencies.
A transitive dependency raised its minimum Node version: inspect the lockfile/change log and either migrate the runtime or select a supported dependency version—never edit installed package syntax in place.
Prevent the error from returning
Declare the supported Node range in
package.jsonand pin the team’s runtime with the version-manager/container mechanism it actually uses.Print runtime versions early in CI logs and fail fast when they fall outside policy.
Commit the lockfile for applications and use reproducible installs.
Track Node.js release status; move production systems before their release line reaches end-of-life.
Inventory background services, IDEs, containers, and deployment images—not only the developer’s interactive shell.
Related Node.js guides
See installing a specific or current Node.js version on Ubuntu for runtime-manager choices.
Update package tooling deliberately with the npm upgrade guide.
Keep project history safe by installing and configuring Git.
Primary references
Node.js publishes the authoritative release status and LTS schedule; production applications should use maintained releases.
Node.js explains the security and ecosystem risks of end-of-life runtimes.
The current ECMAScript modules documentation defines
.mjs,package.jsontype, and CommonJS interoperability.
Comments and corrections