windows
Windows
Configure Proxy#
Environment: Don't Use SetEnvironmentVariable on PATH#
Environment variables live in two registry scopes:
They merge at logon, and PATH is the special case: machine first, user appended (every other variable has the user scope override the machine one).
The value type decides whether %VAR% expands at all:
[System.Environment]::SetEnvironmentVariable writes REG_SZ. One call on PATH bakes every
%VAR% reference into an absolute path — that is exactly why "editing an environment variable
expanded everything". The GUI editor in System Properties does the same.
Specify the type explicitly instead:
Read the unexpanded value (otherwise what you see is already resolved):
Broadcast afterwards so running processes pick it up — new processes only, already-open terminals will not change:
Use variables where variables belong#
The stock machine PATH is written with variables; a machine whose PATH has been through the GUI editor ends up with hardcoded absolute paths. Restoring them means a JDK or CUDA upgrade only touches one variable:
Same for the user scope — everything under %USERPROFILE%\….
Do not convert Program Files to %ProgramFiles%, though: inside a 32-bit process it expands
to Program Files (x86), which causes bugs that are miserable to track down. Stock Windows only
uses variables for system directories.
Use %CUDA_PATH% rather than %CUDA_PATH_V12_9%: the former points at the active version, the
latter is a version-pinned alias and defeats the purpose.
Always compare the expanded value before and after, and roll back on any mismatch:
Installers will revert it#
Fixing it once does not make it stick. [Environment]::SetEnvironmentVariable is the most
convenient API in .NET, so third-party installers reach for it whenever they touch the user
PATH — and even a harmless "read it, confirm we are already listed, write it back unchanged" is
enough to drop the type to REG_SZ and bake every %VAR% into a literal.
Checking the user scope is usually enough; machine-scope installers rarely touch it:
To find the culprit, line the registry key's last-write time up against recently installed program directories — they are usually a minute or two apart:
Rather than repairing it by hand every time, drop a self-heal into $PROFILE so a new shell
fixes it. Only the representation changes — the expanded value stays the same, which makes it
idempotent — and the usual cost is a single registry read:
Do not drop the "only write if the expanded value matches" guard — it is what stops the rewrite from touching an entry whose literal home-directory path was deliberate.
"Untrusted mount point" in SSH Sessions#
On Windows 11 24H2 and later, running certain commands after logging in over SSH fails with:
This generation of Windows tightened how SSH sessions traverse reparse points (symbolic links and junctions). A terminal opened locally is unaffected, so the problem only shows up over ssh.
Confirming it is this#
The phrase "untrusted mount point" is the whole diagnosis — no need to look elsewhere. The decisive variable is the OS version, not configuration:
Three settings identical, only the OS differs — so nothing is misconfigured.
Two dead ends worth skipping: fsutil behavior set SymlinkEvaluation R2L:1 does nothing
here (L2L/R2L describe whether the link and its target are local or remote paths; with both on
C: it is L2L, which is already enabled). The logon token type is not the cause either — the
Win10 box hands out a NETWORK token too and works fine.
Three fixes#
1. Junction blocked → rebuild it with the native API. Junctions made by mklink /J
traverse fine; ones written by a language runtime's own reparse-data code may not:
2. WinGet shim blocked → put the real package directory ahead on PATH. Everything under
WinGet\Links is a symlink; point at WinGet\Packages\<package id> instead:
3. Do installs and upgrades locally or over RDP. pnpm builds node_modules out of a large
number of junctions, and in an SSH session it cannot read back the links it just created, so
the install is bound to fail. Running already-installed software is unaffected.
Switching to password authentication is not worth it — it does yield a full token, but at the cost of passwordless login.