Skip to content

Explain Windows pnpm failures, refuse unreadable package manager scripts, and polish the CLI - #189

Merged
fstubner merged 11 commits into
mainfrom
fix/windows-pnpm-and-cli-polish
Oct 8, 2026
Merged

fstubner merged 11 commits into
mainfrom
fix/windows-pnpm-and-cli-polish

Conversation

@fstubner

@fstubner fstubner commented Oct 8, 2026

Copy link
Copy Markdown
Owner

What changes for someone using nvx

  • nvx allow-host --remove <host[:port]> takes a host back out of allow_hosts, in the file the command adds to. A trusted project file stays trusted, because taking a host out only narrows it. A file nobody had trusted is not trusted by it.
  • nvx --strict tsc says that tsc is a program in the project's node_modules/.bin and prints the working form, nvx --strict shim tsc. It still exits 2 and does not run the program, because running whatever node_modules/.bin holds for a mistyped nvx command would let a cloned project answer it with its own code.
  • bunx and a project's own programs no longer end with [UNDICI-EHPA] Warning. They run on the Node.js that nvx put first on the contained PATH. nvx reads its version from the folder name, so a Node.js that refuses the flag still does not get it.
  • A blocked host no longer ends with a second refusal that offers NVX_YES. The run ends with one line saying it was stopped because nvx refused a connection. The sandbox-did-not-start half of that second refusal was already fixed in Fix Windows stdin-only pipe hang, launch attribute lifetime, and lost scaffolds #188.
  • The line after a refused host stops blaming the host. nvx cannot read the command's output, so it says what happened, keeps exit 77, and adds that allowing the host helps only if the command's own error is about that connection.
  • nvx doctor no longer fails yarn or pnpm for being corepack's launcher. A launcher downloads the package manager the first time it runs, and doctor starts it with no network.
  • A pnpm or yarn kept outside nvx's folders is refused with the fix when it starts through node. It stopped with Cannot find module, exit 1. It is now refused with exit 77, naming the folder to put in allow_read_exec. That folder in allow_read_exec runs it contained.
  • A contained pnpm run that exits 1 or 127 ends with a note naming the two pnpm failures that mention no sandbox, and Known limitations lists them. The 127 panic (Failed to get source volume info) is not a workspace problem. It comes from any install that includes a package with install scripts, with pnpm 9.15.9, 10.34.6 and 11.28.5. No pnpm option avoids it, so it is documented with the ways round.
  • nvx grants reset --all exits 0 when a granted folder was deleted, and withdraws the permission where a renamed folder went. A record now keeps the folder's file ID beside its path. The old exit 1 existed because a path with nothing at it cannot say whether the folder was deleted or renamed. A record from before this change gets an ID on the next contained run, and behaves as it did until then.
  • Known limitations explains why curl.exe fails TLS with CRYPT_E_REVOCATION_OFFLINE and names --ssl-no-revoke. Allowing the certificate authority's host cannot help, because Windows makes that request itself and it never reaches nvx's proxy.

How it was checked

  • gofmt -l internal cmd prints nothing, and go vet ./... passes for windows, linux and darwin.
  • go test ./internal/nvx -count=1 passes on Windows, and so does the same run with NVX_PROBE=1, which launches real AppContainers.
  • The tests that do not need Windows pass in a Linux container (golang:1.26). In the container's full run, TestContainedProcessCannotTypeIntoTheTerminal fails, and it fails the same way on main, because the image has no ip.
  • Each behaviour test fails on main: 13 tests, 3 of them probes that launch a real AppContainer. The tests of the new helper functions do not compile there. Real before and after runs in a scratch NVX_HOME are the measurements in the changelog entries.
  • site: npm run build ends with pagefind's known local Access is denied panic, npm run check:content and the changelog date check pass.

Nothing undid `nvx allow-host` except editing the policy file by hand.
`--remove` takes the host out of `allow_hosts` in the file the command adds to.
A project file that was trusted stays trusted, since removing a host only
narrows it. A file nobody had trusted is not trusted by it.

`nvx --strict tsc` answered "Unknown command: tsc", and the form that works is
`nvx --strict shim tsc`. When the name is a program in the project's
node_modules/.bin, nvx now says so and prints that form with the flags that were
typed. It still exits 2 and does not run the program.
nvx added --disable-warning=UNDICI-EHPA to NODE_OPTIONS only for a command inside
a Node it installed. bunx and a program in node_modules/.bin run on the Node that
a `node` they start finds first on the contained PATH, which is nvx's own, and
each ended with "[UNDICI-EHPA] Warning". The flag now goes in whenever that Node
is one nvx installed and reads NODE_USE_ENV_PROXY. The version comes from the
folder name, and a Node that refuses the flag still does not get it.
…lved

When a host was refused, the step that asks npm what an install brings in failed,
nvx had already explained, and it then asked "Proceed?" with a refusal that told
the reader to set NVX_YES=true. Approving cannot help, since the install goes
through the same host. The run now ends with one line saying it was stopped for
that reason, and -y and NVX_YES do not approve it. A failure nvx has not explained
is still asked about.

The line printed when a command fails after a host was refused no longer says the
host is why: nvx cannot read the command's output. It says what happened and that
allowing the host helps only if the command's own error is about that connection.
…a script it cannot read

doctor no longer fails yarn or pnpm for being corepack's launcher. A launcher
downloads the package manager the first time it runs, and doctor starts it with
no network.

A contained pnpm run that fails on Windows ends with a note naming the two
failures that mention no sandbox. pnpm 12 stops with "Access is denied. (os error
5)" as it reads its --dir argument. pnpm 9, 10 and 11 panic with "Failed to get
source volume info" when an install includes a package that has install scripts,
because they copy it with a native call that asks Windows about the drive's root
folder. No pnpm option avoids that call, and the limitations page now says so,
with the ways round, and why curl.exe fails TLS in the sandbox.

A package manager's script that node is asked to run, from a folder the sandbox
cannot read, is refused with the way to run it. It stopped with "Cannot find
module" and exit 1.
`nvx grants reset --all` exited 1 for a granted folder that was deleted. The record
held only the path, so for a path with nothing at it the reset could not tell a
deleted folder, whose permission went with it, from a renamed one, whose
permission is still in force, and treated both as a failure.

A record now keeps the folder's file ID beside its path. The reset finds a renamed
or moved folder by that ID and withdraws the permission where it is now, and finds
a deleted one gone, with nothing to withdraw. A record written before IDs were kept
gets one the next time a contained run starts with the folder still in
allow_read_exec, and until then behaves as it did.
… wording

The cases that pin what must not change now sit in the tests that cover the
change, so each test fails on the old code. The pnpm note, the project program
hint and the limitations page say what was measured and no more.
… its two exit codes

`nvx allow-host --remove` on a project with no policy file says there is no file,
instead of naming a file that does not exist as one that lacks the host.

The pnpm note follows the two failures it describes, which end with exit 1 and 127,
so an interrupted install does not get it.
…ath is spelled

The path found from a folder's ID is the final path Windows gives back. A folder
reached through a junction, a short name or a substituted drive is spelled
differently there though it has not moved, so a reset reported it as renamed. The
CI runner's temp folder is a short name, and its test failed on that. A folder
whose ID is the one at the recorded path is where it was.
corepack picks pnpm 12 unless the project pins another version, and npm install -g
pnpm installs pnpm 12 today. The limitations page now says to install an earlier
one with npm install -g pnpm@11.
@fstubner
fstubner merged commit 4af5597 into main Oct 8, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant