Repository navigation
Closed
Labels
Issue-BugIssue has been identified as a bug in the productIssue has been identified as a bug in the productUp-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributorsUp-for-grabs issues are not high priorities, and may be opportunities for external contributorsWG-Cmdletsgeneral cmdlet issuesgeneral cmdlet issuesWG-ReviewedA Working Group has reviewed this and made a recommendationA Working Group has reviewed this and made a recommendation
Description
Prerequisites
- Write a descriptive title.
- Make sure you are able to repro it on the latest released version
- Search the existing issues.
- Refer to the FAQ.
- Refer to Differences between Windows PowerShell 5.1 and PowerShell.
Steps to reproduce
(Get-Uptime).GetType().Name # TimeSpan
(Get-Uptime -Since).GetType().Name # DateTime
(Get-Uptime -Since:$false).GetType().Name # DateTime, should be TimeSpanExpected behavior
# TimeSpan
# DateTime
# TimeSpanActual behavior
# TimeSpan
# DateTime
# DateTimeContext
Get-Uptimeemits a[timespan]object by default or[datetime]with-Since.- If
-Sinceis explicitly provided a$falsevalue (-Since:$false/hash table splatting), the type is unexpectedly[datetime].-Since/-Since:$trueand-Since:$falseresult in identical behavior.
Get-Uptimeuses the selected parameter set to determine output type, which is tied to the presence of the-Sinceswitch, not its value.
PowerShell/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUptime.cs
Lines 39 to 49 in df248bd
| switch (ParameterSetName) | |
| { | |
| case TimespanParameterSet: | |
| // return TimeSpan of time since the system started up | |
| WriteObject(uptime); | |
| break; | |
| case SinceParameterSet: | |
| // return Datetime when the system started up | |
| WriteObject(DateTime.Now.Subtract(uptime)); | |
| break; | |
| } |
Despite it being uncommon to specify an explicit $false switch (and arguably against its intention), I believe the current Get-Uptime behavior still amounts to a bug.
- Specifying
$falseis permitted by the language and theGet-Uptimeresult subverts reasonable user expectation. - There are valid use cases for this (e.g., dynamically retrieving the boolean from another source).
- Other cmdlets with parameter sets tied to a switch respect an explicit
$falsevalue (e.g.,'foo' | Select-String f -Raw:$false)
# Simulate dynamic retrieval of the boolean.
$getDateTimeOutput = $false
Get-Uptime -Since:$getDateTimeOutput
# Or...
$params = @{ Since = $getDateTimeOutput }
Get-Uptime @params
# Get-Uptime still unexpectedly emits [datetime] output.
Environment data
Name Value
---- -----
PSVersion 7.6.0-preview.2
PSEdition Core
GitCommitId 7.6.0-preview.2
OS Microsoft Windows 10.0.19045
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0, 5.0, 5.1…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0Reacted by Michael Klement and FireInWinter
Metadata
Metadata
Assignees
Labels
Issue-BugIssue has been identified as a bug in the productIssue has been identified as a bug in the productUp-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributorsUp-for-grabs issues are not high priorities, and may be opportunities for external contributorsWG-Cmdletsgeneral cmdlet issuesgeneral cmdlet issuesWG-ReviewedA Working Group has reviewed this and made a recommendationA Working Group has reviewed this and made a recommendation
Type
Projects
- StatusShow more project fieldsReviewed
Activity
[-]`Get-Uptime -Since` ignores an explict `$false` value[/-][+]`Get-Uptime -Since` ignores an explicit `$false` value[/+]mklement0 commentedon Feb 14, 2025
Good find and good sleuthing.
As an aside, re "arguably against its intention":
Details
As you've already noted, splatting switch parameters with a value of
$falseresults in the equivalent of-SomeSwitch:$false.There is one case where this syntax is required, namely if you want to override a preference variable ad hoc, notably with
-Confirm:$falseTo support the latter case, distinguishing in the command body between the switch having been omitted and having been specified but with a value of
$falseis necessary.Unfortunately, the property name
.IsPresentis confusing, as it reflects the effective value rather than the presence of the switch.(Arguably,
.IsPresentshould have been designed to truly reflect only the presence, alongside a.Valueproperty that reflects the effective value.)It is also debatable how parameter-set selection should work in the face of
-SomeSwitch:$falseif-SomeSwitchbelongs to a distinct parameter set.Arguably, unlike the current behavior, treating
-SomeSwitch:$falseas if-SomeSwitchhad not been specified for the purposes of parameter-set selection would be the more sensible default, given that the preference-variable override-like use case is less common.MartinGC94 commentedon Feb 14, 2025
It's actually quite common that switch parameters that are explicitly set to false don't work as expected. It's usually whenever the Switch is used to specify the parameter set. Here's a few examples:
Here's a snippet to find commands that probably have this issue:
Note that the parameter info is only available for imported modules so the snippet won't find problematic commands if their module hasn't been imported yet.
Ideally people would stop using switches to control parametersets and instead use actual values. For example Dism commands could assume
OnlineAKA the current system by default and only work on images if a path has been specified. I understand why they didn't do this though. If you are working on an image you don't want to accidentally target your own machine because you forgot to specify a path.mklement0 commentedon Feb 14, 2025
Great analysis, MartinGC94; given these results, it sounds like my musing in my previous comment about treating
-SomeFlag:$falseas if-SomeFlaghad not been specified with respect to parameter-set selection would actually fix quite a few bugs - including the one at hand - rather than constituting an unacceptable breaking change.mklement0 commentedon Feb 15, 2025
Ilya (@iSazonov), since I assume that the
Up-for-Grabsdesignation only applies to the immediate fix to be applied toGet-UpTime, I've spun out my suggestion to tackle this problem systemically into a separate feature request / bug report:-Switch:$falsethe same as omitting a switch when selecting a parameter set #25027Implementing the latter would implicitly fix the bug at hand, as well as the bugs that @MartinGC94 mentioned.
(
where Name -EQ:$false Windowsis a special case, whose behavior would change from quietly ignoring the$falsevalue to reporting an error due to the effective absence of an operator).iSazonov commentedon Feb 15, 2025
Up-for-Grabs - we should fix all cmdlets in the repo if they implement a switch in wrong way.
As for declaring this behavior incorrect in general, I cannot agree.
Firstly, the switch was originally implemented as having three states, similar to a nullable boolean. It can be really useful. Let's look at an enum-based parameter. It can switch the set, have multiple possible values, and may have a default value. If there are only two possible values, then this is practically the same as a 3-state switch. We consider this to be useful, just as we do not remove the nullable boolean from C#. Then why should we limit users to the switch options?
As discussed earlier, it would be enough to add some good examples to the documentation.
mklement0 commentedon Feb 15, 2025
Ilya (@iSazonov), I don't fully understand your comment, but note that the three-states argument is a separate issue that is incidental to the linked proposal; with the implementation of the latter, the ability to distinguish the three states will not go away and, conversely, requires custom logic already (the 3 states being: switch passed without argument, switch passed with argument
$false, switch omitted).By contrast, the proposal would make parameter-set selection work the way most users - including the authors of many existing built-in cmdlets - intuitively expect.
Implementing the proposal not only obviates the need to fix - many - of the existing cmdlets individually, it would also prevent authors of future cmdlets from experiencing the same pitfall.
I suggest voicing your concerns regarding the linked proposal there, not here.
surfingoldelephant commentedon Feb 16, 2025
Agreed. I'm guilty of this, also. Nowadays, I often try to rework exclusive switches into a single, enum/set-based parameter and branch off that instead of the parameter set.
Thank you. This revealed 8 additional commands that exhibit the issue. See this comment.
surfingoldelephant commentedon Feb 16, 2025
Thank you for submitting this. I agree, it would be great if we can avoid modifying the individual commands.
In the event the proposal is rejected, there's a question of how to handle the commands individually.
In the case at hand with
Get-Uptime, the fix is very simple, but that is probably not the case with some of the others. It seems a case-by-case evaluation in separate issues will be required, though only if the systematic fix is rejected.$falsevalue passed to a switch parameter #25242SteveL-MSFT commentedon Oct 1, 2025
@PowerShell/wg-powershell-cmdlets agrees this is a bug
logiclrd commentedon Oct 3, 2025
Maybe this is a non-issue or has a trivial fix, but on the surface, it seems to me (a developer with no prior experience in the PowerShell codebase 🙂) that there's a fundamental problem. The problem arises from the fact that the presence of
-Sinceselects a differentParameterSet, and thatParameterSetis documented as returningDateTime. Is it okay for the cmdlet to just return a different type than the one that it looks like it's supposed to be returning?In practice, the cmdlet returning a different type than it's supposed to (per its
[OutputType]attribute) seems to work...Added test of Get-Uptime -Since:$false to Get-Uptime.Tests.ps1, repro…
mklement0 commentedon Oct 4, 2025
Jonathan Gilbert (@logiclrd):
Yes, that a negated switch value too selects the parameter set the switch parameter is associated with is problematic, and Treat
-Switch:$falsethe same as omitting a switch when selecting a parameter set #25027 was an attempt to remedy that - sadly, the proposal there was rejected.The
[OutputType]attribute is purely informative and not enforced by the runtime, which is why the discrepancy you mention is not a problem in practice; quoting from about_Functions_OutputTypeAttribute:MartinGC94 commentedon Oct 4, 2025
This is slightly misleading. The OutputType lets the type inference know what types it can expect which is important for tab completion:
Get-Something | Where <Tab>without an attribute to tell PowerShell what type(s) to expect it can't provide any useful completions. I have created a docs issue to point out this flaw in the documentation: MicrosoftDocs/PowerShell-Docs#12414logiclrd commentedon Oct 4, 2025
Hmm, interesting.
Get-Uptimeis attributed[Cmdlet(DefaultParameterSet = "Timespan")]with[Parameter(ParameterSetName = "Since")]on theSinceswitch. And, it has two[OutputType]attributes mapping"Timespan"totypeof(TimeSpan)and"Since"totypeof(DateTime).If I run
Get-Uptimeon its own, it returns aTimeSpan. And, with no switches, the default ParameterSet of"Timespan"should be selected. This all lines up.But if I type
Get-Uptime | Whereand start pressing tab, it lists the fields ofDateTime. It's treating it as though the"Since"ParameterSet were selected.What's going on? Is this its own bug??
MartinGC94 commentedon Oct 4, 2025
Get-Uptime | Where <Tab>lists the properties for bothDateTimeandTimeSpan. This is because the implementation simply adds all the listed outputtypes without bothering to figure out which ones to add. This is a known limitation in the current implementation: https://github.com/PowerShell/PowerShell/blob/master/src/System.Management.Automation/engine/parser/TypeInferenceVisitor.cs#L1459-L1462I did look into fixing this at one point, but it wasn't as straightforward as I had hoped and I didn't care enough to dig more into it.
logiclrd commentedon Oct 4, 2025
You replied in the middle of me writing this reply 🙂 I had started out with this observation, aligned with what you wrote:
So, the real issue is that if you have a cmdlet
Quxthat has, say, parameter sets"A"and"B", and"A"has aSwitchParameterin it calledFooand"B"has, say, astringparameter calledBar, then you can't doQux -Foo:$false -Bar "Eenp", because the presence of-Foo, even with explicit value$false, will require parameter set"A"while the presence of-Barwill require parameter set"B", and it can't be both at the same time.And this doesn't seem to be something that really comprehensively be "fixed". A narrow fix could explicitly test for the constant value
$falseand passed to a switch and ignore that switch for the purposes of selecting the parameter set. But that would only fix it for the case where someone were explicitly typingMy-Cmdlet -Something:$falseat the command-line, which is, I suspect, basically never a use case. The place where this kind of inference would be more useful is if you're forwarding a switch on as in:(This is a bad example because it only has one parameter -- a better example would use a cmdlet that has parameters that can only be used in one parameter set or another.)
In this case, it cannot statically determine the parameter set; the selection depends on the runtime values. But, the actual implementation of parameter set selection does not use the values, does it? My understanding is that a given statement's parameter set is static to the structure of the statement, regardless of the values provided, and if that's the case, then ignoring switches that have
falsevalues for the purposes of parameter set selection is simply not possible.microsoft-github-policy-service commentedon Oct 9, 2025
📣 Hey @surfingoldelephant, how did we do? We would love to hear your feedback with the link below! 🗣️
🔗 https://aka.ms/PSRepoFeedback