Skip to content

Why do handled exceptions show in ErrorVariable? #3768

Open

Description

@Jaykul

When you use try/catch and handle an exception, it still shows up in the global $Error, and more importantly, in the ErrorVariable.

Is there any way to actually prevent handled exceptions from leaking into the ErrorVariable? Is there any way for a caller to determine that they are not, in fact, errors, but expected results that were dealt with by the function? That is, how does a caller distinguish the errors that matter?

Consider a simple example:

function Count-Parameters {
    [CmdletBinding()]
    param([Parameter(ValueFromRemainingArguments)]$Parameters)
    try {
        $Parameters.FasterMethod()
    } catch {
        $Parameters.Count
    }
}

$Count = Count-Parameters # Everything is fine

$Count = Count-Parameters This One Has Four # Still fine

# But then my paranoid "best practice" fanatic does something like this:
$Count = Count-Parameters -ErrorVariable noCount
if(!$Count -and $NoCount) { 
   Write-Warning $noCount
}

How can I stop the error variable from containing these things that I explicitly handled?

Activity

  1. added
    WG-Enginecore PowerShell engine, interpreter, and runtime
    Issue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get a
    on May 12, 2017
  2. mklement0 commented on May 12, 2017

    @mklement0
    Contributor

    Good catch (accidental pun).

    Note that another quirk comes into play here: -ErrorVariable always returns a [System.Collections.ArrayList] instance, so that you'd have to use Write-Warning $noCount[0], whereas Write-Warning $noCount would actually break, even though only 1 error is returned:

    Write-Warning : Cannot convert 'System.Collections.ArrayList' to the type 'System.String' required by parameter 'Message'. Specified method is not supported.`
    

    -OutVariable, -InformationVariable, and -WarningVariable exhibit the same behavior - see #3773.

  3. BrucePay commented on May 16, 2017

    @BrucePay
    Collaborator

    $Error was originally conceived as a running log of all errors that happened in your script, including exceptions that were caught or when ErrorAction was set to SilentlyContinue The goal was primarily for forensics analysis after your script had been run. (Note: this goal of recording all errors was muted somewhat by the introduction of -ErrorAction ignore where the error isn't written into the error variable.)

  4. SteveL-MSFT commented on May 16, 2017

    @SteveL-MSFT
    Member

    Perhaps a non-breaking change is we could add a IsHandled property to ErrorRecord to help filter?

  5. Jaykul commented on May 16, 2017

    @Jaykul
    ContributorAuthor

    Bruce Payette (@BrucePay) and the -ErrorVariable must mirror that? I mean, I'm ok with everything being in $Error ...

    Steve Lee (@SteveL-MSFT) I guess that would at least give us a way ...

    Right now I do not think there is a way to hide errors, and also tell whether or not a command was successful.

  6. joeyaiello commented on May 24, 2017

    @joeyaiello
    Contributor

    The @PowerShell/powershell-committee had a very interesting conversation around this, it's difficult to know which way we should go with this.

    Could you explain in more detail why this is important to you? Is it simply that you're trying to drill the error variable down to zero as you test your code and try to catch all exceptions?

  7. mklement0 commented on May 25, 2017

    @mklement0
    Contributor

    I obviously can't speak for Joel Bennett (@Jaykul), but here's my 0.0178236 EUR (using today's exchange rate):

    There are two conceivable scenarios (not mutually exclusive):

    • (a) I want to know if any non-terminating error occurred when the cmdlet / advanced function ran - in other words: if the cmdlet / advanced function fully succeeded.

    • (b) I want to examine the specific non-terminating errors emitted, if any, when the cmdlet / advanced function ran.

    (a), even though it should be covered by automatic variable $?, is currently not a reliable option (pet peeve alert), because:

    (b) is currently not a reliable option, because examining the -ErrorVariable-assigned variable or the added-since-before-invocation $Error entries may contain false positives in the form of intentionally caught exceptions that should be considered an implementation detail of the cmdlet / advanced function invoked - that's this issue's complaint.

  8. added
    Breaking-Changebreaking change that may affect users
    Committee-ReviewedPS-Committee has reviewed this and made a decision
    Up-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributors
    and removed
    Review - CommitteeThe PR/Issue needs a review from the PowerShell Committee
    on May 31, 2017
  9. SteveL-MSFT commented on May 31, 2017

    @SteveL-MSFT
    Member

    @PowerShell/powershell-committee reviewed this (Bruce Payette (@BrucePay) was absent from discussion) and agreed this change where -errorvariable only contains unhandled errors makes sense

  10. 5 remaining items

  11. KirkMunro commented on Aug 18, 2017

    @KirkMunro
    Contributor

    Michael Klement (@mklement0) That's really thin. If someone really wants to know if an error occurred when -ErrorAction Ignore is used, they should write their own script that doesn't use -ErrorAction Ignore. Give me a real world use case where someone would want to know if a benign error occurred in a command or script they invoked. You shouldn't care about that when you invoke a command, because at the moment you are invoking that command you are placing your trust that the author of that command will properly let you know if there was an error that you should care about. If you don't trust that, then you shouldn't be invoking that specific command.

    The whole point of -ErrorAction Ignore is to suppress errors. Suppression is of little use for scripts invoked via S.M.A.PowerShell if the invoker has to ignore .HadErrors and check the Errors collection every time (which will be empty). In fact, I'd wager that there are probably many C# binaries out there where PowerShell is invoked from C# with S.M.A.PowerShell that are buggy right now because they rely on .HadErrors identifying whether or not there were actually errors.

    I will log a separate issue for the .HadErrors/$? issue.

  12. mklement0 commented on Aug 18, 2017

    @mklement0
    Contributor

    I will log a separate issue for the .HadErrors/$? issue.

    Great, thanks.

    Other than that, I don't see a disagreement, unless I misunderstood your original comment. You do not want $? and .HadErrors to reflect $False if -ErrorAction Ignore is used, correct? I agree.

    Note that I was talking about SilentlyContinue, not Ignore, in the - definitely exotic - alternative scenario I described.

  13. KirkMunro commented on Aug 18, 2017

    @KirkMunro
    Contributor

    Michael Klement (@mklement0) Whoops, you're right, I misread your previous reply. We're on the same page then.

  14. mklement0 commented on Aug 18, 2017

    @mklement0
    Contributor

    Joel Bennett (@Jaykul): While I'm still unclear on what you were asking, perhaps I can clarify on my end:

    Re logging in $Error / -ErrorVariable:

    My concern fits into the larger issue of the existing, longstanding dichotomy with respect to how non-terminating vs. terminating errors must be handled - see Our Error Handling, Ourselves - time to fully understand and properly document PowerShell's error handling.

    As Bruce Payette (@BrucePay) has pointed out, -ErrorAction Ignore already is a departure from the unconditionally-log-all-exceptions-in-$Error behavior, but is currently only available for non-terminating errors (e.g., Get-Item /NoSuchPath -ErrorAction -Ignore).

    As stated, -ErrorAction Ignore has no effect on a (statement)-terminating error such as Get-Item -NoSuchParam -ErrorAction Ignore (a distinction that strikes me as problematic, especially that using the $ErrorActionPreference preference variable does apply to terminating errors, but it's probably way too late to change that).

    Here's what -ErrorAction Ignore combined with -ErrorVariable currently does:

    > $Error.Clear(); Get-Item /NoSuchPath -ErrorAction Ignore -ErrorVariable ev; $Error.Count; "[$ev]"
    0  # nothing was written to $Error
    [] # nothing was captured in $ev

    Your proposal at least in part asks for the analogous behavior with respect to handling terminating errors, which must be done with Try/ Catch.

    It is the in part aspect that I find problematic:

    For symmetry, conceiving of Try/ Catch as the -ErrorAction Ignore counterpart - or, more accurately, a superset of it - makes sense to me.

    If we only suppress filling the -ErrorVariable variable while continuing to log to $Error, we don't have that symmetry:

    > $Error.Clear(); Invoke-Command { try {1 / 0} catch{} } -ErrorVariable ev; $Error.Count; "[$ev]"
    1  # $Error was still written to, unlike with -ErrorAction Ignore
    [] # in the future: $ev was not assigned to

    Hence my suggestion to not only suppress assigning to the -ErrorVariable variable, but to also suppress logging to $Error.


    Re $? / .HadErrors:

    Kirk Munro (@KirkMunro) then took a step back to bring up a conceptually related issue, arguing that -ErrorAction Ignore - again: applicable to non-terminating errors only - should indicate success, given the explicitly signaled intent to ignore errors - similar to how Try / Catch can be conceived of.

    My aside re try { 1 / 0 } catch {}; $? was meant to show that in the terminating-error realm checking $? after try / catch is virtually pointless, because what $? is set to depends on whether the catch block happens to be empty ($False) or not (whatever statement happens to execute last in the catch block determines the value of $?).

    Taking another step back: $? is problematic in other contexts too:

  15. Jaykul commented on Aug 20, 2017

    @Jaykul
    ContributorAuthor

    I think we're in agreement.

    My request is focused on ErrorVariable only because I was focused on error detection in a script.

    That is, I wasn't concerned with PowerShell.Invoke or any of the rest of this ;-)

    In general, I think it's useless to check $Error in scripts because it's persistent through a whole session, and you don't normally care about errors that happened before your code. Of course, there are lots of ways (not least -ErrorAction SilentlyContinue) that errors show up in $Error and the user doesn't care, but since you're not normally using it, it doesn't matter the same way ErrorVariable does.

    For what it's worth, I would agree that exceptions that are handled in a catch could be left out of $Error as well as ErrorVariable...

  16. microsoft-github-policy-service commented on Jan 4, 2024

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if there is no further activity in 7 days, the issue will be closed automatically.

    Activity in this case refers only to comments on the issue. If the issue is closed and you are the author, you can re-open the issue using the button below. Please add more information to be considered during retriage. If you are not the author but the issue is impacting you after it has been closed, please submit a new issue with updated details and a link to this issue and the original.

  17. microsoft-github-policy-service commented on Jan 4, 2024

    @microsoft-github-policy-service
    Contributor

    This issue has not had any activity in 6 months, if there is no further activity in 7 days, the issue will be closed automatically.

    Activity in this case refers only to comments on the issue. If the issue is closed and you are the author, you can re-open the issue using the button below. Please add more information to be considered during retriage. If you are not the author but the issue is impacting you after it has been closed, please submit a new issue with updated details and a link to this issue and the original.

  18. MatejKafka commented on May 19, 2024

    @MatejKafka
    Contributor

    Ryan Yates (@kilasuit) Please reopen, the issue is unresolved, but triaged and up-for-grabs.

  19. added
    KeepOpenThe bot will ignore these and not auto-close
    and removed
    Resolution-No ActivityIssue has had no activity for 6 months or more
    on May 20, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Breaking-Changebreaking change that may affect usersCommittee-ReviewedPS-Committee has reviewed this and made a decisionIssue-Enhancementthe issue is more of a feature request than a bugIssue-Questionideally support can be provided via other mechanisms, but sometimes folks do open an issue to get aKeepOpenThe bot will ignore these and not auto-closeUp-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributorsWG-Enginecore PowerShell engine, interpreter, and runtime

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions