Skip to content

Clarify the intended behavior/documentation/terminology of terminating errors #4292

Closed

Description

For the larger context, see Our Error Handling, Ourselves - time to fully understand and properly document PowerShell's error handling.

  • The documentation (about_Preference_Variables and about_Throw) only makes the distinction between non-terminating and terminating errors, neglecting to distinguish between the two fundamentally different types of terminating errors:

    • statement-terminating errors, as generated by some cmdlets and by .NET exceptions
    • script-terminating errors, as generated with the Throw keyword or potentially by escalation via -ErrorAction or $ErrorActionPreference.
  • The documentation mistakenly claims that $ErrorActionPreference exclusively applies to non-terminating errors, whereas it controls the behavior of both types of terminating errors too.

On a personal note, the conflation of the two types of terminating errors in the documentation has caused me years of confusion.


The sample cmdlet used below that generates a terminating error is courtesy of PetSerAl.

Statement-terminating errors

Steps to reproduce

# Define New-TerminatingError cmdlet whose sole purpose is to generate a 
# statement-terminating error.
Add-Type '
 using System.Management.Automation; 
 [Cmdlet("New", "TerminatingError")]
 public class NewTerminatingErrorCmdlet : Cmdlet { 
   protected override void ProcessRecord() { 
     ThrowTerminatingError(new ErrorRecord(new System.Exception(), "Error", ErrorCategory.NotSpecified, null));
   } 
 }' -PassThru | % Assembly | Import-Module

# Per the documentation, this should only affect *non-terminating* errors.
$ErrorActionPreference = 'Stop'

# Should only terminate the statement (pipeline), not the script.
New-TerminatingError | % {} -End { 'end of pipeline' }

'end of script'

Expected behavior

Note: Expected based on the current documentation - arguably, the actual behavior makes more sense.

New-TerminatingError : Exception of type 'System.Exception' was thrown.
...
end of script

Actual behavior

$ErrorActionPreference = 'Stop' did take effect for the pipeline-terminating error and aborted the script:

New-TerminatingError : Exception of type 'System.Exception' was thrown.
...

Script-terminating errors

# Per the documentation, this should only affect *non-terminating* errors.
$ErrorActionPreference = 'SilentlyContinue'

# Should abort the script, irrespective of the $ErrorActionPreference value, according
# to the documentation.
Throw "I'm outta here"

'end of script'

Expected behavior

The script should be aborted, and the error message passed to Throw should be displayed.

I'm outta here
...

Actual behavior

$ErrorActionPreference = 'SilentlyContinue' caused the Throw statement to be ignored.

Note that, according to the documentation, Ignore is an invalid value for $ErrorActionPreference and should only be used with the -ErrorAction common parameter.
However, it is accepted and somewhat takes effect: it ignores the Throw error, yet still records it in $Error.

end of script

Environment data

PowerShell Core v6.0.0-beta.4 on macOS 10.12.5
PowerShell Core v6.0.0-beta.4 on Ubuntu 16.04.2 LTS
PowerShell Core v6.0.0-beta.4 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)
Windows PowerShell v5.1.15063.413 on Microsoft Windows 10 Pro (64-bit; v10.0.15063)

Activity

  1. added
    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassif
    on Jul 19, 2017
  2. iSazonov commented on Jul 19, 2017

    @iSazonov
    Collaborator

    Related comment #2860 (comment)

    For posterity, my understanding of terminating errors was wrong. A terminating error cancels execution of the current pipeline or expression, not of an entire script block.

    A better way to check for terminating errors is to just use try/catch:

    C:\Program Files\PowerShell\6.0.0-alpha.18> try {"fdasf" | convertfrom-json} catch {1}
    1
    C:\Program Files\PowerShell\6.0.0-alpha.18> try {ls foo} catch {1}
    ls : Cannot find path 'C:\Program Files\PowerShell\6.0.0-alpha.18\foo' because it does not exist.
    At line:1 char:6
    + try {ls foo} catch {1}
    +      ~~~~~~
        + CategoryInfo          : ObjectNotFound: (C:\Program File....0-alpha.18\foo:String) [Get-ChildItem], ItemNotFound
       Exception
        + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetChildItemCommand
  3. iSazonov commented on Feb 26, 2018

    @iSazonov
    Collaborator

    Michael Klement (@mklement0) If we have this in docs repo I believe we could close the Issue.

  4. mklement0 commented on Feb 26, 2018

    @mklement0
    ContributorAuthor

    Ilya (@iSazonov): Sounds good. Longer-term, I wish we had a proposal to not just document the existing behavior properly, but to simplify error handling, which, needless to say, would be a major breaking change.

  5. iSazonov commented on Feb 26, 2018

    @iSazonov
    Collaborator

    Michael Klement (@mklement0) I guess the best way is to collect such proposals/specifications as RFC to implement them in next version (7.0?).

  6. alx9r commented on Mar 15, 2018

    @alx9r

    Michael Klement (@mklement0) Do you have plans to draft such an RFC? If so, I'd like to follow its progress.

  7. mklement0 commented on Mar 15, 2018

    @mklement0
    ContributorAuthor

    alx9r:

    No immediate plans, given the ongoing discussions, but I'll keep you posted (or vice versa, if you feel inclined to take it on).

    I'd first like to better understand what the original design was (and where things went wrong) and if there's even a fundamental willingness to entertain the thought of a change by the powers that be - which would obviously a massively breaking change.

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

    Area-Maintainers-Documentationspecific to documentation in this repoIssue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifResolution-AnsweredThe question is answered.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions