.\scripts\start-local.ps1 fails on a machine that has everything it needs:
==> Switching Node to latest (this project needs Node 20 or newer)
node v18.16.1
Exception: scripts\NodeVersion.ps1:109
| Node is still v18 after 'nvm use latest'. This project needs Node 20 or newer.
| The newest version nvm has installed is too old. Install a newer one:
| nvm install 24.13.1
The last claim is false, and that is the expensive part. nvm list on this machine reports 26.7.0, 24.13.1 and 18.16.1 — two versions well past the floor. The message sends the reader to install something they already have.
Root cause
nvm use latest does not mean "the newest version I have installed". nvm-windows resolves latest against the remote release list. From its own help:
nvm use [version] [arch] : Switch to use the specified version. Optionally use "latest", "lts", or "newest".
"newest" is the latest installed version.
newest is the alias that means the newest installed. latest means the newest that exists. Reproduced directly:
$ nvm use latest
26.8.1
activation error: Version not installed. Run "nvm ls" to see available versions.
$ node --version
v18.16.1
$ nvm use newest
Now using node v26.7.0 (64-bit)
So the switch never happened. NodeVersion.ps1's docstring states the wrong semantics — "nvm use latest picks the highest version nvm has installed locally" — and the code was written against that.
Why the error blamed the wrong thing
Use-Node verifies the switch took, which is the right instinct, but it special-cased the alias:
With $Version of latest that short-circuits to true regardless of what is running. So Use-Node swallowed nvm's activation error: Version not installed — which it had already captured in $output for exactly this purpose — and returned success holding v18.16.1. Use-NodeLatest then checked the major and reported the one explanation that was definitely wrong.
Both halves have to change. Fixing only the alias moves the bug rather than removing it: newest is not latest, so the special case stops matching and Use-Node compares '26.7.0' -eq 'newest', fails twice and throws even though the switch worked. Confirmed by doing exactly that.
Fix
Ask for newest, and correct the docstring to record what the two aliases actually mean.
Verify an alias switch against nvm's own output rather than assuming it worked, so a genuine failure reports nvm's reason instead of a guess about installed versions.
Verified from a real v18.16.1 baseline: latest (not installed here) now throws and surfaces nvm's reason; Use-NodeLatest switches 18.16.1 → 26.7.0; a concrete uninstalled version still throws correctly. start-local.ps1 then runs the whole way through — database, migrations, backend on 3000.
Affects both entry points
NodeVersion.ps1 is shared by start-local.ps1 and run-tests.ps1 deliberately, so this broke both and one change fixes both.
Open question
There is a hardcoded Use-Node -Version '26.7.0' in the working tree that takes a different approach: it pins the version rather than asking for the newest installed. That works today but it pins a version that has to be maintained by hand and goes stale when 26.7.0 is uninstalled, and Use-NodeLatest is named and documented as "newest installed". Worth settling before this lands.
`.\scripts\start-local.ps1` fails on a machine that has everything it needs:
```
==> Switching Node to latest (this project needs Node 20 or newer)
node v18.16.1
Exception: scripts\NodeVersion.ps1:109
| Node is still v18 after 'nvm use latest'. This project needs Node 20 or newer.
| The newest version nvm has installed is too old. Install a newer one:
| nvm install 24.13.1
```
The last claim is false, and that is the expensive part. `nvm list` on this machine reports 26.7.0, 24.13.1 and 18.16.1 — two versions well past the floor. The message sends the reader to install something they already have.
## Root cause
`nvm use latest` does not mean "the newest version I have installed". nvm-windows resolves `latest` against the **remote** release list. From its own help:
```
nvm use [version] [arch] : Switch to use the specified version. Optionally use "latest", "lts", or "newest".
"newest" is the latest installed version.
```
`newest` is the alias that means the newest installed. `latest` means the newest that exists. Reproduced directly:
```
$ nvm use latest
26.8.1
activation error: Version not installed. Run "nvm ls" to see available versions.
$ node --version
v18.16.1
$ nvm use newest
Now using node v26.7.0 (64-bit)
```
So the switch never happened. `NodeVersion.ps1`'s docstring states the wrong semantics — "`nvm use latest` picks the highest version nvm has installed locally" — and the code was written against that.
## Why the error blamed the wrong thing
`Use-Node` verifies the switch took, which is the right instinct, but it special-cased the alias:
```powershell
if ($Version -eq 'latest' -or $raw.TrimStart('v') -eq $Version.TrimStart('v')) {
```
With `$Version` of `latest` that short-circuits to true regardless of what is running. So `Use-Node` swallowed nvm's `activation error: Version not installed` — which it had already captured in `$output` for exactly this purpose — and returned success holding v18.16.1. `Use-NodeLatest` then checked the major and reported the one explanation that was definitely wrong.
Both halves have to change. Fixing only the alias moves the bug rather than removing it: `newest` is not `latest`, so the special case stops matching and `Use-Node` compares `'26.7.0' -eq 'newest'`, fails twice and throws even though the switch worked. Confirmed by doing exactly that.
## Fix
- Ask for `newest`, and correct the docstring to record what the two aliases actually mean.
- Verify an alias switch against nvm's own output rather than assuming it worked, so a genuine failure reports nvm's reason instead of a guess about installed versions.
Verified from a real v18.16.1 baseline: `latest` (not installed here) now throws and surfaces nvm's reason; `Use-NodeLatest` switches 18.16.1 → 26.7.0; a concrete uninstalled version still throws correctly. `start-local.ps1` then runs the whole way through — database, migrations, backend on 3000.
## Affects both entry points
`NodeVersion.ps1` is shared by `start-local.ps1` and `run-tests.ps1` deliberately, so this broke both and one change fixes both.
## Open question
There is a hardcoded `Use-Node -Version '26.7.0'` in the working tree that takes a different approach: it pins the version rather than asking for the newest installed. That works today but it pins a version that has to be maintained by hand and goes stale when 26.7.0 is uninstalled, and `Use-NodeLatest` is named and documented as "newest installed". Worth settling before this lands.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
.\scripts\start-local.ps1fails on a machine that has everything it needs:The last claim is false, and that is the expensive part.
nvm liston this machine reports 26.7.0, 24.13.1 and 18.16.1 — two versions well past the floor. The message sends the reader to install something they already have.Root cause
nvm use latestdoes not mean "the newest version I have installed". nvm-windows resolveslatestagainst the remote release list. From its own help:newestis the alias that means the newest installed.latestmeans the newest that exists. Reproduced directly:So the switch never happened.
NodeVersion.ps1's docstring states the wrong semantics — "nvm use latestpicks the highest version nvm has installed locally" — and the code was written against that.Why the error blamed the wrong thing
Use-Nodeverifies the switch took, which is the right instinct, but it special-cased the alias:With
$Versionoflatestthat short-circuits to true regardless of what is running. SoUse-Nodeswallowed nvm'sactivation error: Version not installed— which it had already captured in$outputfor exactly this purpose — and returned success holding v18.16.1.Use-NodeLatestthen checked the major and reported the one explanation that was definitely wrong.Both halves have to change. Fixing only the alias moves the bug rather than removing it:
newestis notlatest, so the special case stops matching andUse-Nodecompares'26.7.0' -eq 'newest', fails twice and throws even though the switch worked. Confirmed by doing exactly that.Fix
newest, and correct the docstring to record what the two aliases actually mean.Verified from a real v18.16.1 baseline:
latest(not installed here) now throws and surfaces nvm's reason;Use-NodeLatestswitches 18.16.1 → 26.7.0; a concrete uninstalled version still throws correctly.start-local.ps1then runs the whole way through — database, migrations, backend on 3000.Affects both entry points
NodeVersion.ps1is shared bystart-local.ps1andrun-tests.ps1deliberately, so this broke both and one change fixes both.Open question
There is a hardcoded
Use-Node -Version '26.7.0'in the working tree that takes a different approach: it pins the version rather than asking for the newest installed. That works today but it pins a version that has to be maintained by hand and goes stale when 26.7.0 is uninstalled, andUse-NodeLatestis named and documented as "newest installed". Worth settling before this lands.