Audit and repair your Windows PATH.
Your PATH rots. Uninstallers leave folders behind, installers add themselves twice, and two copies of the same tool end up fighting over which one runs. pathfix finds all of it, tells you which copy actually wins, and cleans up safely.
.\pathfix.ps1Reports by default and changes nothing. -Fix writes, and every write is
journaled to disk before it is applied so -Undo works even after a crash
or a reboot.
If you have ever installed ffmpeg, Python or Node and then been told the command doesn't exist — or worse, watched the wrong version run — the cause is almost always PATH. pathfix answers both questions in one pass:
- Is the folder even there? Broken entries pile up silently after uninstalls.
- Which copy actually runs? If
python.exesits in three PATH folders, only the first one wins. pathfix names the winner and lists the ones it hides.
This is the part most PATH cleaners get wrong, and it is permanent.
Windows stores PATH as REG_EXPAND_SZ, so entries like %SystemRoot%\system32
stay as written and get expanded at use time. The obvious way to write it back
from PowerShell is [Environment]::SetEnvironmentVariable, which expands
every variable first and stores the result as plain text.
On the machine this was developed on, 5 of the 8 system PATH entries were variable references:
%SystemRoot%\system32
%SystemRoot%
%SystemRoot%\System32\Wbem
%SYSTEMROOT%\System32\WindowsPowerShell\v1.0\
%SYSTEMROOT%\System32\OpenSSH\
A naive "cleanup" bakes all five into literal C:\WINDOWS\... paths. It usually
still works — until the drive letter or the Windows folder differs, and then it
does not. pathfix reads and writes the raw registry value and preserves the
REG_EXPAND_SZ type, so what you wrote is what stays.
Verified directly:
BEFORE raw = %USERPROFILE%\Documents;...\good1;...\nope1;...\good1
AFTER raw = %USERPROFILE%\Documents;...\good1
UNEXPANDED %USERPROFILE% SURVIVED : True
kind still ExpandString : True
what a naive tool would have stored instead:
C:\Users\hayyz\Documents <- variable gone for good
- Dry run by default. Without
-Fix, nothing is written. - Journal first. The old PATH is written to
%LOCALAPPDATA%\pathfix\before the registry is touched. If the journal cannot be written, pathfix refuses to make the change. -Undorestores byte for byte, including the original registry value type.- Undo refuses to clobber newer edits. If you changed PATH after pathfix ran, it stops and tells you instead of throwing your change away.
- User scope by default. Machine scope needs
-Scope Machineand an elevated prompt; without elevation it says so and skips rather than failing. - It only ever removes broken, duplicate and empty entries. Entries that need a judgement call — a file instead of a folder, a relative path, a variable that is not set — are reported and left alone.
# audit the User PATH
.\pathfix.ps1
# audit everything a new process actually sees
.\pathfix.ps1 -Scope Both
# clean it up
.\pathfix.ps1 -Fix
# changed your mind
.\pathfix.ps1 -UndoWindows PowerShell 5.1 (ships with Windows 10/11) or PowerShell 7+. No dependencies, no installer, no network access. One file.
| Option | Default | What it does |
|---|---|---|
-Scope |
User |
User, Machine or Both |
-Fix |
off | Apply the repairs instead of just reporting |
-Undo |
— | Restore PATH from the most recent journal entry |
-History |
— | List journal entries available to undo |
-Prune |
all | Which categories to remove: Broken, Duplicates, Empty |
-NoShadow |
off | Skip the shadowed-command scan (the slowest part) |
-Json |
off | Emit the report as JSON |
-PathString |
— | Audit a PATH string you paste in. Read-only |
-RegistryKey |
— | Testing hook: audit a different key instead of the real PATH |
Want to remove broken folders but keep duplicates? -Fix -Prune Broken.
A genuine read-only audit of the development machine, -Scope Both:
pathfix v1.0.0
----------------------------------------------------------------
Machine 8 entries, 273 chars, ExpandString
User 2 entries, 50 chars, ExpandString
Problems
----------------------------------------------------------------
[ ok ] no broken folders
[ ok ] no duplicate entries
[warn] 1 empty entry(s) - a stray semicolon
an empty PATH entry means "the current folder", which is a mild security risk.
[ ok ] PATH length 323 chars
scanning for shadowed commands...
scanned 9 folders in 0.6s
[warn] 1 command(s) exist in more than one PATH folder
notepad (3 copies)
runs -> C:\WINDOWS\system32\notepad.exe
hidden C:\WINDOWS\notepad.exe
hidden C:\Users\hayyz\AppData\Local\Microsoft\WindowsApps\notepad.exe
the first folder in PATH order wins. This is why the wrong python runs.
Summary
----------------------------------------------------------------
healthy entries 9
removable 1
This was a dry run. Nothing was changed.
Run with -Fix to remove the entries listed above.
That shadow result was cross-checked against Windows' own resolver:
where.exe notepad
1. C:\Windows\System32\notepad.exe <- pathfix called this the winner
2. C:\Windows\notepad.exe
3. C:\Users\hayyz\AppData\Local\Microsoft\WindowsApps\notepad.exe
Same winner, same order, same count.
A synthetic PATH was built with an exactly known set of defects, so every number could be checked rather than eyeballed:
| Defect planted | Count | pathfix reported |
|---|---|---|
| Broken folders | 3 | 3 |
| Duplicates (exact, trailing slash, different case) | 3 | 3 |
| Empty entries | 2 | 2 |
| Entry pointing at a file | 1 | 1 |
| Relative entry | 1 | 1 |
| Entry using an unset variable | 1 | 1 |
| Healthy entries | 5 | 5 |
| Total | 16 | 16 |
A tool.exe was placed in three folders and a solo.exe in only one.
pathfix reported exactly one shadowed command with three copies, named the
first-in-PATH copy as the winner, ignored solo.exe, and ignored a notes.txt
that was not executable.
The write path was tested against a scratch registry key so the real PATH was never involved:
- fix → undo produced a byte-identical value, with
REG_EXPAND_SZand every%VARIABLE%intact - editing PATH after a fix and then running
-Undomade pathfix stop and preserve the newer edit instead of restoring over it - an empty PATH, a PATH with no problems, and Machine scope without elevation all behaved without a stack trace
Do I need to reboot? No. pathfix broadcasts the environment-change message Windows uses, so new terminals pick it up. Already-open terminals keep their old copy — that is how Windows works, not a pathfix limitation.
Is it safe to run -Fix on the Machine PATH?
It is journaled and undoable like everything else, but it needs an elevated
prompt and it affects every user on the machine. Read the dry run first.
Why is an empty entry a security risk?
An empty PATH entry means "the current directory". Any folder you cd into can
then supply an executable that runs ahead of the real one.
It says my PATH is over 2047 characters. The modern limit is much larger, but plenty of older installers and batch tooling still truncate at 2047 and fail in confusing ways. Removing duplicates and broken folders usually gets you back under.
Does it phone home? No network calls at all.
- obs-4k60-recorder — record 4K60 in OBS without dropped frames
- framecheck — diagnose a recording before you edit it
- clipmine — find the loud moments in a recording and cut them out
- gamemode — free up your machine before a session
- diskscout — find what is eating your disk
- dupefind — find duplicate files by content
MIT — see LICENSE.