Repository navigation
ActionPreference.Inquire is inconsistent and needs some redesign #9755
Copy link
Copy link
Closed
Labels
Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get aideally support can be provided via other mechanisms, but sometimes folks do open an issue to get aResolution-No ActivityIssue has had no activity for 6 months or moreIssue has had no activity for 6 months or moreWG-Enginecore PowerShell engine, interpreter, and runtimecore PowerShell engine, interpreter, and runtime
Description
Transcript demonstrating the problem
As the title says, ErrorAction.Inquire needs work. For example, consider the following transcript:
PS C:\> $ErrorActionPreference = 'Inquire'
PS C:\> gps -id 9999999999999999
Action to take for this exception:
Cannot bind parameter 'Id'. Cannot convert value "9999999999999999" to type "System.Int32". Error: "Value was either too large or too small for an Int32."
[C] Continue [I] Silently Continue [B] Break [S] Suspend [?] Help (default is "C"): B
Get-Process : Cannot bind parameter 'Id'. Cannot convert value "9999999999999999" to type "System.Int32". Error: "Value was either too large or too small for an Int32."
At line:1 char:9
+ gps -id 9999999999999999
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-Process], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetProcessCommand
PS C:\> try {gps -id 9999999999999999} catch {}
PS C:\> gps -id ([int]::MaxValue)
Confirm
Cannot find a process with the process identifier 2147483647.
[Y] Yes [A] Yes to All [H] Halt Command [S] Suspend [?] Help (default is "Y"): H
gps : The running command stopped because the user selected the Stop option.
At line:1 char:1
+ gps -id ([int]::MaxValue)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [Get-Process], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ActionPreferenceStop,Microsoft.PowerShell.Commands.GetProcessCommand
PS C:\> $WarningPreference = 'Inquire'
PS C:\> Write-Warning 'boo'
WARNING: boo
Confirm
Continue with this operation?
[Y] Yes [A] Yes to All [H] Halt Command [S] Suspend [?] Help (default is "Y"): h
Write-Warning : The running command stopped because the user selected the Stop option.
At line:1 char:1
+ Write-Warning 'boo'
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [Write-Warning], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ActionPreferenceStop,Microsoft.PowerShell.Commands.WriteWarningCommand
This transcript demonstrates the following issues:
- The message you receive when prompted as a result of
ActionPreference.Inquirediffers depending on the event that occurred. For terminating errors, users are properly asked what action they would like to take for the exception. For non-terminating errors, users are shown the heading "Confirm", with an error message if the Inquire prompt was displayed as a result of a non-terminating error, or a question asking users if they want to continue if the Inquire prompt was displayed as a result of a non-error message (warning, debug, verbose, progress). - If a terminating error is the cause for the message, the prompt appears too late, such that it will not appear at all if a terminating error is suppressed by a naked catch statement. This makes it difficult for users to properly troubleshoot errors in the system, because they may have something being suppressed that they don't know about.
- The options users can choose from differ depending on whether or not the Inquire prompt appeared due to a terminating exception or a non-terminating message, even though the underlying actions taken from those options are the same.
- For non-terminating messages (non-terminating errors, or debug, verbose, warning or progress messages), the options are simply incorrect. There is no "Yes to All" functionality for these messages from Inquire. "Yes to All" is really "Silently Continue", except for these messages it's not silent at all. For everything except non-terminating errors, the message is displayed first. For non-terminating errors, the message is displayed after the Inquire prompt. So "Yes to All" aka "SilentlyContinue" is just broken.
- The usefulness of the
[S] Suspendoption becomes questionable once PR Break action preference #8205 is approved/merged. This point is a little forward thinking, but if we can enter the debugger in response to an error, terminating or not (something which is now possible as part of that PR), then the value of entering a nested prompt diminishes greatly and it would be more valueable to have a[D] Debugoption that enters the debugger instead (this keeps the user in the current scope, lets them look at the location in the script, step through the code, continue execution, terminate the script, etc.).
Expected behavior
- Whether
ActionPreference.Inquireis checked because of a terminating error or a non-terminating message...- ...the options presented to users should be identical, as follows:
[C] Continue [I] Silently Continue [S] Stop [D] Debug [?] Help (default is "C"):
This includes the following changes:[B] Breakhas been replaced with[S] Stopto matchActionPreference.Stop.[S] Suspendhas been replaced with[D] Debugto allow users to enter the debugger (this depends on PR Break action preference #8205).
- ...the prompt and the options presented to users should appear before the corresponding message is shown in all cases so that the behavior is consistent, and so that the message can be suppressed with
[I] Silently Continue(which should work).
- ...the options presented to users should be identical, as follows:
- For non-terminating messages resulting in
ActionPreference.Inquiredisplaying a prompt...- ...the prompt caption should state
Action to take for this [error|warning]:orAction to take for this [verbose|debug|progress] message:. - ...the prompt message should show the actual message that will be displayed.
- ...the prompt caption should state
- For terminating messages resulting in
ActionPreference.Inquiredisplaying a prompt...- ... the check should happen earlier (like the check to enter the debugger in PR Break action preference #8205) so that users can receive the prompt even for exceptions that don't appear.
With these changes in place, the transcript above would instead show as follows:
PS C:\> $ErrorActionPreference = 'Inquire'
PS C:\> gps -id 9999999999999999
Action to take for this exception:
Cannot bind parameter 'Id'. Cannot convert value "9999999999999999" to type "System.Int32". Error: "Value was either too large or too small for an Int32."
[C] Continue [I] Silently Continue [S] Stop [D] Debug [?] Help (default is "C"): s
Get-Process : Cannot bind parameter 'Id'. Cannot convert value "9999999999999999" to type "System.Int32". Error: "Value was either too large or too small for an Int32."
At line:1 char:9
+ gps -id 9999999999999999
+ ~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-Process], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetProcessCommand
PS C:\> try {gps -id 9999999999999999} catch {}
Action to take for this exception:
Cannot bind parameter 'Id'. Cannot convert value "9999999999999999999" to type "System.Int32". Error: "Value was either too large or too small
for an Int32."
[C] Continue [I] Silently Continue [S] Stop [D] Debug [?] Help (default is "C"): s
Get-Process : Cannot bind parameter 'Id'. Cannot convert value "9999999999999999999" to type "System.Int32". Error: "Value was either too large or too small for an Int32."
At line:1 char:14
+ try {gps -id 9999999999999999999} catch {}
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Get-Process], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : CannotConvertArgumentNoMessage,Microsoft.PowerShell.Commands.GetProcessCommand
PS C:\> gps -id ([int]::MaxValue)
Action to take for this error:
Cannot find a process with the process identifier 2147483647.
[C] Continue [I] Silently Continue [S] Stop [D] Debug [?] Help (default is "C"): s
gps : The running command stopped because the user selected the Stop option.
At line:1 char:1
+ gps -id ([int]::MaxValue)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [Get-Process], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ActionPreferenceStop,Microsoft.PowerShell.Commands.GetProcessCommand
PS C:\> $WarningPreference = 'Inquire'
PS C:\> Write-Warning 'boo'
Action to take for this warning:
boo
[C] Continue [I] Silently Continue [S] Stop [D] Debug [?] Help (default is "C"): s
Write-Warning : The running command stopped because the user selected the Stop option.
At line:1 char:1
+ Write-Warning 'boo'
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (:) [Write-Warning], ParentContainsErrorRecordException
+ FullyQualifiedErrorId : ActionPreferenceStop,Microsoft.PowerShell.Commands.WriteWarningCommand
Actual behavior
See transcript shown above.
Environment data
Name Value
---- -----
PSVersion 6.2.0
PSEdition Core
GitCommitId 6.2.0
OS Microsoft Windows 10.0.17763
Platform Win32NT
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
WSManStackVersion 3.0
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get aideally support can be provided via other mechanisms, but sometimes folks do open an issue to get aResolution-No ActivityIssue has had no activity for 6 months or moreIssue has had no activity for 6 months or moreWG-Enginecore PowerShell engine, interpreter, and runtimecore PowerShell engine, interpreter, and runtime
Activity
KirkMunro commentedon May 29, 2019
I'd like to fix these issues once #8205 is accepted/merged, assuming y'all are OK with them Steve Lee (@SteveL-MSFT). Since these changes pertain to interactive use only (
ActionPreference.Inquireresponses cannot be scripted), they would be non-breaking, and they would catch theActionPreference.Inquirefeature up to the latest functionality while fixing a bundle of issues in it.bpayette commentedon May 29, 2019
Kirk Munro (@KirkMunro) First - thanks for the detailed write-up! Second - I haven't had coffee yet so I'm going to ask some stupid questions which you've probably already answered. My main question is why should the inquire prompt have the same behaviour for different kinds of messages? For example, offering "Yes to all" makes no sense for a terminating error but it definitely makes sense for non-terminating errors. The goal of
Inquirewas to inspect the messages and then take appropriate action based on that message - skip the message, skip all of the messages, halt execution or enter a subshell/debugger to examine the state of the system to see what's happening, etc. But the set of operations needs to change for different types of messages. What am I missing?KirkMunro commentedon May 29, 2019
Thanks for coming back so quickly with questions, Bruce Payette (@bpayette). Since I don't necessarily have all of the history behind why things are the way they are, I often just look at the end user experience. I tried many ways to get
Inquirewith a response ofYes to Allto actually suppress all further prompts without any success whatsoever. For example, I tried the following, with and without SupportsShouldProcess, and was always prompted for each item, in both PowerShell 5.1 and 6.2 (sorry that I don't have any older versions handy to test this with right now).I'm fine if there need to be some differences between terminating/non-terminating support based on what is possible, but those differences need to be rationalized for today (since this functionality was in version 1.0 and pre-dates the PowerShell debugger), the bugs need to be fixed, the functionality should tie into the debugger instead of having suspend, and the wording and choices needs to be cleaned up.
I had thought what you described was the original intent until I ran a bunch of tests to figure out why I haven't tried using this functionality in many years, which usually stems from running into issues and realizing it's just broken). Even then, though, if it was working that way, that makes
Inquirework like a conditionalConfirmfor non-terminating messages, vs like a light-weight debugger prompt for terminating messages? It's certainly confusing. I don't have my heart set on one approach or the other, as long as it is consistent at the least (with obvious technical differences) and rational.Here's an example showing how the wording presented right now for non-terminating errors is just odd:
Confirm what? That it cannot find that process? And the help doesn't really help. Here's what that says:
Is
[Y] Yesreally a step like it sounds in the description (i.e. "only the next step of the operation")? That's not accurate. Is[A] Yes to Allreally continuing "with all steps of the operation"? That's not entirely accurate either. Doesn't that just mean continue, like in the debugger?Plus where options can be consistent (i.e.
HaltvsBreak), they should be consistent.I'll think about this some more with the idea that users should be able to run a command without further prompts if desired. Maybe that should be an explicit option that is very clear in the choices: "Run to completion" (maybe ignoring Inquire prompts of any type at that point, not just for the specific message type that occurred).
KirkMunro commentedon May 29, 2019
Just to share more questions while they are top of mind, to drive this closer to a solution:
ActionPreference.Inquire?[C] Continue [P] Continue without further prompts [S] Stop [D] Debug [?] Help (default is "C"):This answers your question about why they should have the same prompting (terminating vs non-terminating). You're right,
Yes to Alldoesn't make sense for terminating. ButContinuemakes sense to both, andContinue without further prompts(or better wording) also makes sense to both.This also highlights the confusion with the wording, because what you guys meant by
Silently Continuehere may have beenContinue without further prompts, but those are two different things, are they not? Because it wouldn't suppress errors likeSilentlyContinuedoes...it would just stop theInquireprompting from occurring.BrucePay commentedon May 31, 2019
Kirk Munro (@KirkMunro)
If you're running a script and you're looking for the nth exception, skipping them until you get to the right one makes sense to me. But you could also
Continuewithout much difference in effect."Continue without further prompts" doesn't make any sense. It's not going to continue the current operation because it was a terminating error. (BTW - the context of these prompts is the current command/pipeline, not the script running the pipeline.)
Anyway, it sounds like there are significant bugs in the implementation. "We" :-) should probably fix the bugs first before deciding to change the behaviour based on a faulty impression of how things work.)
KirkMunro commentedon Jun 6, 2019
Bruce Payette (@BrucePay)
I'm not completely in agreement with you on that point. I would suggest the following instead:
ActionPreference.Inquire. I'm just talking about documentation here. Based on this discussion I think I'm closer to understanding the use cases, but it would be worth putting them to text to make sure it is all covered. You're right that we shouldn't make changes based on a faulty impression of how it works.That last point is key for me because
ActionPreference.Inquirepre-dates the PowerShell debugger, and I believe while it's design may have been well thought out for PowerShell 1.0, I feel it quickly became antiquated and of questionable value in PowerShell 2.0 and later. Since we're approaching PowerShell 7.0, it seems like a great time to turn this questionable-valued feature (that I even suggested in another discussion could probably be yanked from PowerShell, given it's very limited usefulness and buggy implementation) into something that people can and will use. I'd much prefer spending some time on an RFC on a new design that makes more sense forPowerShell 7and implementing that design than waste time fixing bugs in a feature that "nobody uses" (I literally know no one -- 12 years of working with PowerShell in the community, and aside from when someone would run intoActionPreference.Inquirebehavior as a result of-Debug, which has since been updated such that-Debugnow just sets$DebugPreferencetoActionPreference.Continue, I've never seen a single person explicitly useActionPreference.Inquire).Thoughts?
KirkMunro commentedon Jun 10, 2019
Just to add a data point to this: I tweeted a question about
ActionPreference.InquireThursday last week, which was retweeted a number of times, and not a single person responded. Literally, crickets, so I followed up on it, and have had some responses, confirming that they don't use the feature. It will be interesting to see if other responses show up or not...microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 16, 2023
This issue has not had any activity in 6 months, if this is a bug please try to reproduce on the latest version of PowerShell and reopen a new issue and reference this issue if this is still a blocker for you.
microsoft-github-policy-service commentedon Nov 23, 2023
This issue has been marked as "No Activity" as there has been no activity for 6 months. It has been closed for housekeeping purposes.
SamErde commentedon Aug 13, 2024
I came here looking for more information about how to effectively use -Inquire but it seems like
[System.Management.Automation.Host.ChoiceDescription]and$Host.UI.PromptForChoice()may be more useful.