Search code examples
powershellpathwinget

Cannot run winget although it is in the path


I cannot run winget although it is in the path :

PS C:\> $env:path -split ';' | sls WindowsApps

%USERPROFILE%\AppData\Local\Microsoft\WindowsApps


PS C:\> 

and winget is present in the %USERPROFILE%\AppData\Local\Microsoft\WindowsApps\ directory :

PS C:\> ls $env:USERPROFILE\AppData\Local\Microsoft\WindowsApps\winget.exe


    Directory: C:\Users\sebastien.mansfeld\AppData\Local\Microsoft\WindowsApps


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a---l        15/11/2023     18:13              0 winget.exe


PS C:\> winget.exe
winget.exe : The term 'winget.exe' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path
was included, verify that the path is correct and try again.
At line:1 char:1
+ winget.exe
+ ~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (winget.exe:String) [], CommandNotFoundException
    + FullyQualifiedErrorId : CommandNotFoundException

PS C:\>

I was expecting winget to run because it's in the PATH.

Why is this not working ?


Solution

  • tl;dr

    Fix your user-level Path environment-variable definition in the registry as follows:

    & {
      param($key)
      Set-ItemProperty -Type ExpandString $key Path (Get-ItemPropertyValue $key Path) 
    } registry::HKEY_CURRENT_USER\Environment
    
    • In order for future PowerShell sessions to see this change, either log off and back on, or - if acceptable - kill all explorer.exe instances to force a restart of the Windows (GUI) shell, using Stop-Process -Name explorer.exe

    • You may also want to call the above with registry::HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment to fix the machine-level definitions, however:

      • Only do so if you find the registry value to be of type REG_SZ (instead of the expected REG_EXPAND_SZ)

        • Given your symptom, this condition is implied for your user-level definition (see next section).

        • Generally, it takes extra effort to retrieve the unexpanded data stored in a REG_EXPAND_SZ-typed registry value - see this answer.

      • Doing so requires running with elevation (as administrator).


    Background information:

    If the process-level instance of the Path environment variable - as reflected in $env:Path - contains unexpanded cmd.exe-style environment-variable references such as %USERPROFILE%, the implication is that the registry-based definition(s) of the variable was corrupted.

    That is, the original REG_EXPAND_SZ-typed definition of the Path environment variable in the registry was accidentally changed to REG_SZ, which can easily happen if you use utilities such as setx.exe or the System.Environment.SetEnvironmentVariable .NET API to update these definitions.

    • For a robust way to update the persistent definition of $env:PATH - which is lamentably cumbersome - see this answer.

    The process-level value of the Path environment variable is the concatenation of the expanded value of the composite value of the Path values of the following registry keys, in order:

    • HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment (machine-level)
    • HKEY_CURRENT_USER\Environment (user-level)
    • Note that this implies that machine-level entries take precedence.

    If the registry values were accidentally changed to REG_SZ (literal strings), no expansion of cmd.exe-style environment-variable references such as %USERPROFILE% takes place.

    Since attempts to invoke executables by name only (e.g. winget.exe) use the directories listed in the value of Path verbatim, a directory literally named %USERPROFILE%\AppData\Local\Microsoft\WindowsApps, for instance, won't be found.

    Changing the type of the registry value back to REG_EXPAND_SZ (ExpandString in .NET terms), as shown in the top section, ensures that cmd.exe-style environment-variable references are again automatically expanded before they're stored in the process-level Path value.