You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflict on module import #21199
I recently experienced a problem with importing a newer version of Microsoft.PowerShell.PSResourceGet (v1.0.2) than what is included with PowerShell 7.4.1 (v1.0.1):
Is this intentional? Can this cause the issue I experience with PSResourceGet?
My understanding of the issue
It seems that when Import-Module searches through $env:PSModulePath for available modules to import, and stumbles across a module that is not contained in a directory consisting of its' version number, it will lazily import assemblies and thus block importing of any other version of the same module.
Ways to mitigate this
Three different ways to get Import-Module -Name 'Microsoft.PowerShell.PSResourceGet' -Version 1.0.2 to work.
Delete the included module: %ProgramW6432%\PowerShell\7\Modules\Microsoft.PowerShell.PSResourceGet
The directories that you listed at the ones that are part of the MSI install and should be static or managed by the MSI and updated with a new MSI. I suggest you should not be overwriting those.
Your installed modules should go in, say in my case D:\OneDrive\Documents\PowerShell\Modules. Here install-module does give them the module-name/version directory structure.
D:\> tree D:\OneDrive\Documents\PowerShell\Modules
Folder PATH listing for volume data512
Volume serial number is 3EE9-A4CC
D:\ONEDRIVE\DOCUMENTS\POWERSHELL\MODULES
├───rhubarb-geek-nz.CancellationTokenEvent
│ └───1.0.1
├───rhubarb-geek-nz.Forever
│ └───0.0.2
├───rhubarb-geek-nz.MySqlConnection
│ └───2.3.1
├───rhubarb-geek-nz.NpgsqlConnection
│ └───7.0.6
├───rhubarb-geek-nz.OracleConnection.Core
│ └───3.21.120
├───rhubarb-geek-nz.PowerShellDataFile
│ └───1.0.0
├───rhubarb-geek-nz.SqlConnection
│ └───1.0.4
└───rhubarb-geek-nz.SQLiteConnection
└───1.0.118.0
├───linux-arm
├───linux-arm64
├───linux-x64
├───osx-arm64
├───osx-x64
├───win-arm
├───win-arm64
├───win-x64
└───win-x86
If installing modules in user context/scope and you have OneDrive for Business (ODfB) Known Folder Move (KFM) enabled, PSResourceGet, PowerShellGet and PackageManagement will install modules to %OneDrive%\Documents\PowerShell\Modules% (due to the folder redirection by ODfB KFM), and thus sync thousands of small files to OneDrive.
I don't want this behavior, so I've made a directory %LOCALAPPDATA%\Microsoft\PowerShell\Modules where I install modules using Save-PSResource as Install-PSResource does not care about $env:PSModulePath and instead uses hardcoded paths for user and system context.
PS C:\Users\olavb>Get-Module-ListAvailable 'Microsoft.PowerShell.PSResourceGet'|Format-List-Property 'Name','Version','Path'
Name : Microsoft.PowerShell.PSResourceGet
Version : 1.0.1
Path : C:\program files\powershell\7\Modules\Microsoft.PowerShell.PSResourceGet\Microsoft.PowerShell.PSResourceGet.psd1
Name : Microsoft.PowerShell.PSResourceGet
Version : 1.0.2
Path : C:\Users\olavb\AppData\Local\Microsoft\PowerShell\Modules\Microsoft.PowerShell.PSResourceGet\1.0.2\Microsoft.PowerShell.PSResourceGet.psd1
PS C:\Users\olavb>
Then, importing v1.0.2 gives:
PS C:\Users\olavb>Import-Module-Name 'Microsoft.PowerShell.PSResourceGet'-RequiredVersion 1.0.2Import-Module: Assembly with same name is already loaded
PS C:\Users\olavb>
This indicates to me that something outside of my preferences and settings are having issues. What it is I don't know.
Yes, actually. v7.4.1 also got Microsoft.PowerShell.ThreadJob v2.0.3. Only that it's in directory ThreadJob:
If renaming that directory to Microsoft.PowerShell.ThreadJob, and renaming the psd1 file too, we get the following (with a newer version installed outside this dir):
PS C:\Users\olavb>Get-Module-ListAvailable 'Microsoft.PowerShell.ThreadJob'|Format-List-Property 'Name','Version','Path'
Name : Microsoft.PowerShell.ThreadJob
Version : 2.0.3
Path : C:\program files\powershell\7\Modules\Microsoft.PowerShell.ThreadJob\Microsoft.PowerShell.ThreadJob.psd1
Name : Microsoft.PowerShell.ThreadJob
Version : 2.1.0
Path : C:\Users\olavb\AppData\Local\Microsoft\PowerShell\Modules\Microsoft.PowerShell.ThreadJob\2.1.0\Microsoft.PowerShell.ThreadJob.psd1
PS C:\Users\olavb>
And voila:
PS C:\Users\olavb>Import-Module-Name 'Microsoft.PowerShell.ThreadJob'-RequiredVersion '2.1.0'Import-Module: Assembly with same name is already loaded
PS C:\Users\olavb>
Ergo:
This is module independent.
If adding a <version> directory for the PowerShell included/embedded modules, import works as expected.
PS C:\Users\olavb>Get-Module-ListAvailable 'Microsoft.PowerShell.ThreadJob'|Format-List-Property 'Name','Version','Path'
Name : Microsoft.PowerShell.ThreadJob
Version : 2.0.3
Path : C:\program files\powershell\7\Modules\Microsoft.PowerShell.ThreadJob\2.0.3\Microsoft.PowerShell.ThreadJob.psd1
Name : Microsoft.PowerShell.ThreadJob
Version : 2.1.0
Path : C:\Users\olavb\AppData\Local\Microsoft\PowerShell\Modules\Microsoft.PowerShell.ThreadJob\2.1.0\Microsoft.PowerShell.ThreadJob.psd1
PS C:\Users\olavb>Import-Module-Name 'Microsoft.PowerShell.ThreadJob'-RequiredVersion '2.1.0'
PS C:\Users\olavb>
Thus this GitHub issue is still valid as far as I understand.
So yes, adding a profile script to change the order of PSModulePath could be a workaround. But it shouldn't be neccessary for basic functionality one expects to work.
So yes, adding a profile script to change the order of PSModulePath could be a workaround. But it shouldn't be neccessary for basic functionality one expects to work.
That is consistent with how paths normally work. I could have five different versions of pwsh.exe on my system, but the one that is found first in PATH is the one that will be run.
Now you could argue that it should read all the entries in all the module path directories and then select the one with the highest version. That would be sensible but currently I don't think that is implemented.
That is consistent with how paths normally work. I could have five different versions of pwsh.exe on my system, but the one that is found first in PATH is the one that will be run.
Except that it works as expected if you add a <version> directory to the PowerShell embedded modules.
PowerShell 7 muddies the water a bit as it also pulls in the System and AllUsers from WinPS but that's more for backwards compatibility support.
The System scope is modules bundled specifically with the PowerShell install. You aren't meant to touch these as it's what PowerShell technically ships with. This does not have version specific directories as it's only ever meant to contain 1 version of the module. The AllUsers and CurrentUser scopes are what Install-Module, Install-PSResource can manage and contains versioned module entries.
While you aren't meant to touch the System ones it seems like there might be an assembly conflict happening here stopping you from loading the newer version. I’ll have to take a closer look to see what that is and why PowerShell is still loading the existing module when a newer one should theoretically be preferred.
I've just done a test, you can install the version side by side but as the module loads a binary and .NET only allows one version of the same binary to be loaded it will fail when you attempt to import a newer version in the same process. This is only the case when trying to import straight after upgrading. Starting a fresh new process works just fine though as it will favour the new version.
PowerShell 7.4.1
PS C:\Users\vagrant-domain>Import-module Microsoft.PowerShell.PSResourceGet -PassThru
ModuleType Version PreRelease Name ExportedCommands
-----------------------------------------------
Binary 1.0.1 Microsoft.PowerShell.PSResourceGet {Find-PSResource,Get-InstalledPSResource,Get-PS…
PS C:\Users\vagrant-domain>Install-module-Name Microsoft.PowerShell.PSResourceGet -Scope AllUsers -Force
PS C:\Users\vagrant-domain>Import-module Microsoft.PowerShell.PSResourceGet -PassThru
Import-Module: Assembly with same name is already loaded
PS C:\Users\vagrant-domain> pwsh { Import-module Microsoft.PowerShell.PSResourceGet -PassThru }
ModuleType Version PreRelease Name ExportedCommands
-----------------------------------------------
Binary 1.0.2 Microsoft.PowerShell.PSResourceGet {Find-PSResource,Get-InstalledPSResource,Get-PS…
TLDR: You can have newer versions that are favoured over the package provided ones, you just can't load multiple versions in the same process.
Try to change PSModulePath in process context to have the path to the packaged version first Jordan Borean (@jborean93), then try importing the newest version. Then loading the newest version fails, even in a fresh session, even is specifying -RequiredVersion. Somehow the packaged assembly gets loaded in the process.
I use a non-standard path because of OneDrive for Business KFM. Then my path, even though I've added it to PSModulePath in user context, gets added as one if the lasts paths in PSModulePath in process context. Related issue:
changed the title [-]PowerShell included modules does not have a version directory like Windows PowerShell had[/-][+]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflic on import[/+]on Feb 28, 2026
changed the title [-]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflic on import[/-][+]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflic on module import[/+]on Feb 28, 2026
Why can't included modules just be added to a versioned directory so we don't have to deal with this?
I created this quick and dirty script to do this after installation:
Click to expand
<#.NOTES Author: Olav Rønnestad Birkeland | github.com/o-l-a-v Created: 2026-02-28 Modified: 2026-02-28 It seems that when `Import-Module` searches through `$env:PSModulePath` for available modules to import, and stumbles across a module that is not contained in a directory consisting of its' version number, it will lazily import assemblies and thus block importing of any other version of the same module. * Triggers this: `Modules/<module_name>/<module_name>.psd1` * Does not trigger this: `Modules/<module_name>/<module_version>/<module_name>.psd1` Related: * 2025-02-08: <https://github.com/PowerShell/PowerShell/issues/21201> * 2024-02-08: <https://github.com/PowerShell/PowerShell/issues/21199> Thoughts on how to fix: Put included modules in a versioned directory. * Do not use `Get-Module` to find the module version as it might lazy load the module, which will block moving it. * Thus parse "<module_name>.psd1" manually..EXAMPLE . $psEditor.GetEditorContext().CurrentFile.Path -ModulesDirectory ( 'C:\Users\olavb\scoop\apps\pwshp\7.6.0-rc.1\Modules' ) -WhatIf#># Input and expected output
[CmdletBinding(SupportsShouldProcess)]
[OutputType([System.Void])]
Param(
[Parameter(Mandatory)]
[ValidateNotNullOrWhiteSpace()]
[ValidateScript({[System.IO.Directory]::Exists($_)})]
$ModulesDirectory
)
# PowerShell preferences$ErrorActionPreference='Stop'$InformationPreference='Continue'# Find info$Modules= [array](Get-ChildItem-Path $ModulesDirectory-Directory)
$ModulesDetails= [PSCustomObject[]](
$Modules.ForEach{
[PSCustomObject]@{
'Name'= [string] $_.'Name''Path'= [string] $_.'FullName''Psd1'= [System.IO.Path]::Combine($_.'FullName', [string]::Format('{0}.psd1'-f$_.'Name'))
}
}.ForEach{
Add-Member-InputObject $_-PassThru -NotePropertyMembers @{
'Psd1Exists'= [bool]([System.IO.File]::Exists($_.'Psd1'))
}
} |Sort-Object-Property 'Name'
)
# Move modules into a versioned directoryforeach ($Modulein$ModulesDetails) {
# Output current moduleWrite-Information-MessageData (
'{0}# {1}'-f$(if($ModulesDetails.IndexOf($Module-ne0)){[System.Environment]::NewLine}),$Module.'Name'
)
# Failproof: Make sure Psd1 file exists at expected pathif (-not$Module.'Psd1Exists') {
Write-Information-MessageData (
'"{0}" was not found, has probably been processed already.'-f$Module.'Psd1'
)
Continue
}
# Get version from Psd1 file$Version= [System.Version](
(
[System.IO.File]::ReadAllLines($Module.'Psd1').Where(
{
-not [string]::IsNullOrWhiteSpace($_) -and$_.Trim() -match'^ModuleVersion\s{0,}='
},'First'
)[0] -as [string] -replace'\s{1,}'-replace'''','"'
).Split('"')[1]
)
# Failproof: Don't continue if version could not be parsedif ([string]::IsNullOrWhiteSpace($Version)) {
Write-Information-MessageData 'Failed to find version.'Continue
}
# Generate new path$NewPath= [System.IO.Path]::Combine($Module.'Path',$Version)
# Find items to be moved$ItemsToMove= [string[]](
(Get-ChildItem-Path $Module.'Path').'FullName'
)
if ($PSCmdlet.ShouldProcess($NewPath,'Move')) {
$null= [System.IO.Directory]::CreateDirectory($NewPath)
$ItemsToMove.ForEach{
$null=Move-Item-Path $_-Destination $NewPath
}
Write-Information-MessageData ('Successfully moved files into "{0}".'-f$NewPath)
}
}
changed the title [-]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflic on module import[/-][+]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflict on module import[/+]on Feb 28, 2026
Until the module is decoupled from PSCore itself, there's no value to have it in a versioned folder since it's not updatable outside of updating PSCore itself. The ones that have versioned folders are those obtained from PSGallery.
This is by design for maintenance and supportability reasons
Ryan Yates (@kilasuit): I guess the fix is to fix the installed modules version lookup to not lazy load assemblies then, or whatever it does. Maybe it could do that discovery in a child process?
It seems to me that PowerShell is using a brute force module discovery mechanism that works for looking for executables on a PATH but not modules in nested subtrees. I suggest the problem is the promise that you can just add a module by copying into a directory on the path and PowerShell will eventually find it and use it.
Does PowerShell manage a persistent index for the modules it knows about?
If PowerShell maintained an index in a simple database where manifest files were added and removed by Install-Module/Install-PSResource/Uninstall-Module/Uninstall-PSResource etc then cmdlets etc would be found by referring to the manifests in the index rather than exhaustive searches in the file system.
Activity
rhubarb-geek-nz commentedon Feb 8, 2024
Have a look at $env:PSModulePath, for example mine is
The directories that you listed at the ones that are part of the MSI install and should be static or managed by the MSI and updated with a new MSI. I suggest you should not be overwriting those.
Your installed modules should go in, say in my case
D:\OneDrive\Documents\PowerShell\Modules. Here install-module does give them the module-name/version directory structure.o-l-a-v commentedon Feb 8, 2024
rhubarb-geek-nz
Sure. Here's my scenario though:
PSResourceGet,PowerShellGetandPackageManagementwill install modules to%OneDrive%\Documents\PowerShell\Modules%(due to the folder redirection by ODfB KFM), and thus sync thousands of small files to OneDrive.%LOCALAPPDATA%\Microsoft\PowerShell\Moduleswhere I install modules usingSave-PSResourceasInstall-PSResourcedoes not care about$env:PSModulePathand instead uses hardcoded paths for user and system context.I end up with:
Microsoft.PowerShell.PSResourceGet%ProgramW6432%\PowerShell\7\Modules\Microsoft.PowerShell.PSResourceGet\<content>Microsoft.PowerShell.PSResourceGet%LOCALAPPDATA%\Microsoft\PowerShell\Modules\Microsoft.PowerShell.PSResourceGet\1.0.2\<content>Starting a fresh pwsh.exe v7.4.1 window gives me the following:
And
Then, importing v1.0.2 gives:
This indicates to me that something outside of my preferences and settings are having issues. What it is I don't know.
rhubarb-geek-nz commentedon Feb 8, 2024
It may be a chicken-and-egg problem with PSResourceGet where that is loaded early on before it starts recognizing PSModulePath.
Can you do show the same scenario with a different module?
o-l-a-v commentedon Feb 8, 2024
Yes, actually. v7.4.1 also got
Microsoft.PowerShell.ThreadJobv2.0.3. Only that it's in directoryThreadJob:If renaming that directory to
Microsoft.PowerShell.ThreadJob, and renaming the psd1 file too, we get the following (with a newer version installed outside this dir):And voila:
Ergo:
<version>directory for the PowerShell included/embedded modules, import works as expected.rhubarb-geek-nz commentedon Feb 8, 2024
In your path
Your 1.0.2 in
AppData\Localis after 1.0.1 inpowershell\7in the path sequence.So it finds 1.0.1 before 1.0.2
If 1.0.2 was in
C:\Users\olavb\Documents\PowerShell\Modulesthen does it find it?o-l-a-v commentedon Feb 8, 2024
I don't know how
$env:PSModulePathin process context is stitched together.If I set my preferred module path first; yes, that also works.
So yes, adding a profile script to change the order of PSModulePath could be a workaround. But it shouldn't be neccessary for basic functionality one expects to work.
rhubarb-geek-nz commentedon Feb 8, 2024
That is consistent with how paths normally work. I could have five different versions of pwsh.exe on my system, but the one that is found first in PATH is the one that will be run.
Now you could argue that it should read all the entries in all the module path directories and then select the one with the highest version. That would be sensible but currently I don't think that is implemented.
o-l-a-v commentedon Feb 8, 2024
Except that it works as expected if you add a
<version>directory to the PowerShell embedded modules.Example:
%ProgramW6432%\PowerShell\7\Modules\Microsoft.PowerShell.PSResourceGet\<content>%ProgramW6432%\PowerShell\7\Modules\Microsoft.PowerShell.PSResourceGet\1.0.1\<content>So there's currently two workarounds I know of:
<version>directory%ProgramW6432%\PowerShell\7\Modules\<modulename>\<content>%ProgramW6432%\PowerShell\7\Modules\<modulename>\<version>\<content>rhubarb-geek-nz commentedon Feb 8, 2024
You can set PSModulePath in your environment before you run pwsh.exe if that helps.
I would avoid changing the contents of MSI installed directories. If you upgrade you will get the version-less directory back again.
o-l-a-v commentedon Feb 8, 2024
Yes, which makes this a bug with PowerShell IMO.
I agree. This was more of a proof that theres a breaking change between how Windows PowerShell included modules vs. how PowerShell does it. :)
jborean93 commentedon Feb 8, 2024
AFAIK There are three types of directories included in
PSModulePathPowerShell 7 muddies the water a bit as it also pulls in the System and AllUsers from WinPS but that's more for backwards compatibility support.
The
Systemscope is modules bundled specifically with the PowerShell install. You aren't meant to touch these as it's what PowerShell technically ships with. This does not have version specific directories as it's only ever meant to contain 1 version of the module. TheAllUsersandCurrentUserscopes are whatInstall-Module,Install-PSResourcecan manage and contains versioned module entries.While you aren't meant to touch the
Systemones it seems like there might be an assembly conflict happening here stopping you from loading the newer version. I’ll have to take a closer look to see what that is and why PowerShell is still loading the existing module when a newer one should theoretically be preferred.jborean93 commentedon Feb 9, 2024
I've just done a test, you can install the version side by side but as the module loads a binary and .NET only allows one version of the same binary to be loaded it will fail when you attempt to import a newer version in the same process. This is only the case when trying to import straight after upgrading. Starting a fresh new process works just fine though as it will favour the new version.
TLDR: You can have newer versions that are favoured over the package provided ones, you just can't load multiple versions in the same process.
o-l-a-v commentedon Feb 9, 2024
Try to change PSModulePath in process context to have the path to the packaged version first Jordan Borean (@jborean93), then try importing the newest version. Then loading the newest version fails, even in a fresh session, even is specifying
-RequiredVersion. Somehow the packaged assembly gets loaded in the process.I use a non-standard path because of OneDrive for Business KFM. Then my path, even though I've added it to PSModulePath in user context, gets added as one if the lasts paths in PSModulePath in process context. Related issue:
%OneDriveCommercial%if OneDrive for Business Known Folder Move (KFM) is enabled PSResourceGet#627Edit: One more way to mitigate this; override path in
Import-Module -Namelike so:o-l-a-v commentedon Feb 9, 2024
If I understand this correctly: Generation of PSModulePath does not work as documented here:
Documentation says it will add PSModulePath in user context first if it exists. Which it does for me. Still
$PSHOMEgets prioritized over it.While Windows PowerShell functions as I'd expect in this regard.
If PSModulePath in user context was prioritized over
$PSHOME\Modulesloading newer versions of PSResourceGet and ThreadJob would work as expected.o-l-a-v commentedon Feb 9, 2024
Seems PowerShell is hardcoded to prioritize built in modules over PSModulePath:
PowerShell/src/System.Management.Automation/engine/Modules/ImportModuleCommand.cs
Line 2021 in 0919240
[-]PowerShell included modules does not have a version directory like Windows PowerShell had[/-][+]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflic on import[/+][-]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflic on import[/-][+]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflic on module import[/+]o-l-a-v commentedon Feb 28, 2026
Two years have gone by.
Why can't included modules just be added to a versioned directory so we don't have to deal with this?
I created this quick and dirty script to do this after installation:
Click to expand
[-]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflic on module import[/-][+]PowerShell included modules does not have a version directory like Windows PowerShell had, causing assembly conflict on module import[/+]kilasuit commentedon Jun 23, 2026
This has been previously discussed in #2350
This is by design for maintenance and supportability reasons
o-l-a-v commentedon Jun 23, 2026
Ryan Yates (@kilasuit): I guess the fix is to fix the installed modules version lookup to not lazy load assemblies then, or whatever it does. Maybe it could do that discovery in a child process?
rhubarb-geek-nz commentedon Jun 23, 2026
It seems to me that PowerShell is using a brute force module discovery mechanism that works for looking for executables on a PATH but not modules in nested subtrees. I suggest the problem is the promise that you can just add a module by copying into a directory on the path and PowerShell will eventually find it and use it.
Does PowerShell manage a persistent index for the modules it knows about?
If PowerShell maintained an index in a simple database where manifest files were added and removed by Install-Module/Install-PSResource/Uninstall-Module/Uninstall-PSResource etc then cmdlets etc would be found by referring to the manifests in the index rather than exhaustive searches in the file system.