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
Save-PSResource doesn't install the correct version. #1657
I am experiencing a problem with Save-PSResource. When installing a specific version of a module from a NuGet server, it doesn't take the one specified.
Steps to reproduce:
Have one powershell module in a NuGet server with two different versions published, like '2024.5.20.1' and 2024.5.20.12'
ExampleModule installed in:C:\Users\ExampleUser\Documents\ExampleModule\2024.5.20.1\ExampleModule.psd1C:\Users\ExampleUser\Documents\ExampleModule\2024.5.20.1\ExampleModule.psm1 Inside the ExampleModule.psd1, the field ModuleVersion with the correct version, in this case '2024.5.20.1'
Actual behavior
ExampleModule installed in:C:\Users\ExampleUser\Documents\ExampleModule\2024.5.20.1\ExampleModule.psd1C:\Users\ExampleUser\Documents\ExampleModule\2024.5.20.1\ExampleModule.psm1 Inside the ExampleModule.psd1, the field ModuleVersion with an incorrect version, in this case '2024.5.20.12'
Error details
No response
Environment data
PSVersion 7.4.2
PSEdition Core
GitCommitId 7.4.2
OS Microsoft Windows 10.0.20348
Platfor Win32NT
PSCompatibleVersions {1.0,2.0,3.0,4.0....}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Its not the same scenario. I think the problem is that instead of taking the version '2024.5.20.1' as exact version, is taking it as minimum version. That's why then the version '2024.5.20.12' is taken.
I also cannot reproduce the problem, at least with the PSWriteColor module.
The -Version parameter is explicitly documented as being version-exact (no need for [...] enclosure), and that to get the minimum-compatible-version behavior you'd have to use something like '[1.0.0,]' (note the comma).
This allows prefix collisions. For example, when versions 1.2.3 and 1.2.30 exist, the packageContent URL for 1.2.30 also contains the requested normalized version string 1.2.3. Because responses are version-ordered and the loop stops at the first match, Save-PSResource -Version 1.2.3 can download the 1.2.30 payload while still writing it under the requested 1.2.3 destination.
This also explains why exact range syntax does not prevent the problem: by this point the specification has already been parsed into the same normalized NuGetVersion, and the final selection is still substring-based.
The download selection should compare the registration entry’s parsed version to the requested NuGetVersion, as FindVersionHelper does, rather than searching for the version text anywhere in the packageContent URL. Both the synchronous and asynchronous paths need the same change and regression coverage for prefix-adjacent versions.
Reacted by Olav Rønnestad Birkeland and Yoeri Rousseaux
Activity
237dmitry commentedon May 20, 2024
I could not reproduce:
ADD-Izan-Garcia commentedon May 20, 2024
Its not the same scenario. I think the problem is that instead of taking the version '2024.5.20.1' as exact version, is taking it as minimum version. That's why then the version '2024.5.20.12' is taken.
237dmitry commentedon May 20, 2024
A did not find modules with similar versions:
mklement0 commentedon May 20, 2024
I also cannot reproduce the problem, at least with the
PSWriteColormodule.The
-Versionparameter is explicitly documented as being version-exact (no need for[...]enclosure), and that to get the minimum-compatible-version behavior you'd have to use something like'[1.0.0,]'(note the comma).mklement0 commentedon May 20, 2024
On second thought:
The Package Versioning rules seem to be based on semver version numbers, which are limited to three components, with an optional suffix.
Therefore,
2024.5.20.1is not a valid semver ([semver]) version number (but is a valid[version]number).I wonder if the problem is related to that, and how such invalid-as-semver version numbers are de facto / should be handled.
o-l-a-v commentedon Jul 24, 2024
Example module from PowerShell Gallery with NuGet versioning (four digits vs. semver three, ref: https://learn.microsoft.com/en-us/nuget/concepts/package-versioning?tabs=semver20sort#where-nugetversion-diverges-from-semantic-versioning):
Older than latest version:
Using `-Version '1.0.1.314'` works as expected:
Using `-Version [1.0.1.314]` worked too in my testing (as in, did install that exact version):
Not an issue? Might be an API issue with whatever backend OP used? I used PSResourceGet v1.0.5 against PSGallery.
et1975 commentedon Aug 19, 2026
I believe the root cause is in the NuGet V3 download path rather than version-range parsing.
FindVersionHelpercompares parsedNuGetVersionvalues exactly, butInstallHelperselects thepackageContentURL using a substring comparison:https://github.com/PowerShell/PSResourceGet/blob/3f0d2e5be761f9a7677a5162d6200447733c1460/src/code/V3ServerAPICalls.cs\#L918-L928
The asynchronous path contains the same comparison:
https://github.com/PowerShell/PSResourceGet/blob/3f0d2e5be761f9a7677a5162d6200447733c1460/src/code/V3ServerAPICalls.cs\#L1002-L1012
This allows prefix collisions. For example, when versions
1.2.3and1.2.30exist, thepackageContentURL for1.2.30also contains the requested normalized version string1.2.3. Because responses are version-ordered and the loop stops at the first match,Save-PSResource -Version 1.2.3can download the1.2.30payload while still writing it under the requested1.2.3destination.This also explains why exact range syntax does not prevent the problem: by this point the specification has already been parsed into the same normalized
NuGetVersion, and the final selection is still substring-based.The download selection should compare the registration entry’s parsed version to the requested
NuGetVersion, asFindVersionHelperdoes, rather than searching for the version text anywhere in thepackageContentURL. Both the synchronous and asynchronous paths need the same change and regression coverage for prefix-adjacent versions.