Agent just scaffolded a project from 2020

waldekm1 pts0 comments

Your agent just scaffolded a project from 2020 - Microsoft for Developers

Skip to main content

Search<br>Search

No results

Cancel

Waldek Mastykarz

Principal Developer Advocate

Your agent ran a scaffold command. Project generated, dependencies resolved, no errors. Everything looks fine. Except it’s based on the project structure from 2020, and neither you nor the agent noticed.

How npx picks the right-but-wrong version

When an agent scaffolds a project or runs a CLI tool, it often reaches for npx without specifying a version. Something like:

npx create-some-app my-project

Notice, that there’s no version pinned anywhere. The agent typed the package name and assumed it’d get the latest. That’s where things break.

When you run npx without a version, npm resolves the latest version that’s compatible with your current Node runtime. Since npm-pick-manifest v9.1.0 (shipped mid-2024), npm prioritizes versions whose engines field matches your Node version over the latest tag. If an old version has no engine constraints at all, npm considers it compatible with everything.

Say a package has these two versions on the registry:

// v2.0.0 (latest)<br>{ "engines": { "node": ">=22.14.0<br>On Node 24, npm skips v2.0.0 (upper bound excludes 24) and lands on v1.3.0, because no engines means "works everywhere." The resolution chain looks like this:

Step<br>What npm does<br>Result

Check latest tag (v2.0.0)<br>node — skip

Check next highest (v1.9.0)<br>node — skip

Check v1.3.0<br>No engines field — compatible

Install v1.3.0<br>Done

This is how npx works for everyone, agents included. It reads package.json, not docs. It doesn’t know that a newer version exists and would work fine if you just had the right Node version. It follows the engine constraints mechanically, and if those constraints exclude your runtime, it moves on to the next compatible version. This behavior surprised some folks when it first shipped, but it’s been in npm since 10.8.2.

That’s the trap: the scaffold succeeds, the files look fine, nothing flags the mismatch. It all seems to work, just with something ancient.

The Node version manager trap

Things get worse if you use a Node version manager like fnm or nvm.

Version managers let you install multiple Node versions and easily switch between them. It’s convenient: you can run Node 22 for one project and Node 24 for another. But agents don’t think about this. When the agent opens a terminal and runs npx, it gets whatever Node version happens to be active. It doesn’t look for version requirements in the package it’s about to install. Instead, it uses the Node version that’s readily available.

So if you switched to Node 24 for a different project yesterday and forgot to switch back, your agent is now scaffolding with a Node version that pushes npm into its fallback behavior. The agent doesn’t know better. It ran the command, got output, and kept going.

How would you even catch this? npx succeeds, the files appear, the agent reports success. You’d have to inspect the generated package.json, notice the version mismatch, trace it back to your Node version, and connect that to npm’s engine resolution. That’s a lot of detective work for something that looks like it worked.

Old versions become a catch-all

Specifying engine constraints is the right thing to do. It keeps your package off runtimes you haven’t tested, and no one should stop doing it. But there’s a catch.

Any package that’s been around long enough will likely have older versions that didn’t specify engine constraints. Those unconstrained versions are technically compatible with every Node version, because they never said otherwise. So when npm looks for a compatible version, the latest unconstrained version becomes the catch-all package that npx will use.

This doesn’t affect every package equally. Most popular packages (Next.js, ESLint, Prettier, Angular CLI) use open-ended lower bounds like "node": ">=20". Those cover any newer Node version, so npm resolves to the latest just fine. The problem hits packages that use tight upper bounds ("node": ">=22.14.0 ) or caret-bounded ranges ("node": "^16 || ^14 || ^12"). Those ranges exclude Node versions outside the tested range, which is exactly when npm starts looking for older alternatives.

Enterprise and platform tools are more likely to use this pattern. They certify specific Node LTS versions and explicitly exclude untested runtimes. Responsible engineering, but it means agents get silently downgraded when they run npx without pinning a version.

And the trend is toward more engine constraints, not fewer. Package authors are increasingly dropping support for older runtimes, and that’s good practice. But it also means more versions of more packages will trigger this fallback when paired with a Node version that’s outside the supported range.

Why agents get hit the hardest

A developer running npx might notice the version mismatch. Maybe the scaffold output mentions v1.11, maybe the generated files look...

version node agent package versions project

Related Articles