Powershell: Implement New-ErrorRecord Cmdlet

Created on 23 Feb 2017  路  14Comments  路  Source: PowerShell/PowerShell

Currently Write-Error has many parameters to create a rich ErrorRecord and write it to the error channel as a non-terminating error.

Throw allows us to generate a terminating error and provide a string for the exception message, or an ErrorRecord object to pass, but without getting into New-Object territory there is no easy way to create an error record and pass to Throw.

New-ErrorRecord would allow us to generate the error details and then choose to write or throw it as necessary.

See https://gist.github.com/wpsmith/e8a9c54ca1c7c741b5e9 as an example Cmdlet.

See also this search for 1683 examples of this pattern:
https://github.com/search?utf8=%E2%9C%93&q=%22New-Object+System.Management.Automation.ErrorRecord%22+language%3APowerShell&type=Code&ref=searchresults

Issue-Enhancement WG-Engine

Most helpful comment

Let me try to summarize and make a suggestion:

  • In _PowerShell code_, Write-Error, via its -Category, -ErrorId, ... parameters, already offers a way to create custom ErrorRecord instances, by either wrapping a _custom error message_ (-Message) or a _preexisting exception_ (-Exception) - though the latter is currently broken - see #10774.

    • However, creating such a custom ErrorRecord is currently tied solely to writing directly to the error stream, i.e. limited to creating a _non-terminating_ error.

    • Introducing something like a -AsTerminatingError switch as suggested by @Jaykul (or @iSazonov's simpler alternative name, -Throw, which I like for its association with the Throw statement) would remove this limitation, to allow the custom error record to be thrown as a script-terminating (runspace-terminating) error as well, like a customized Throw call.

      • As an aside: A _script_-terminating error is the only kind of terminating error you can emit from PowerShell code ( unless you use $PSCmdlet.ThrowTerminatingError directly, which scripters should not be expected to need to use); conversely, (binary) cmdlets _cannot_ raise script-terminating errors themselves and can only create _statement_-terminating ones, via Cmdlet.ThrowTerminatingError() or by throwing or not catching a raw exception.
    • Someone who really wants to create a custom ErrorRecord _in isolation_, for later use - which is clearly an advanced use case - can use New-Object System.Management.Automation.ErrorRecord or [System.Management.Automation.ErrorRecord]::new() directly, as needed.

  • _Separately_, in parallel, _for SDK users_ (in C#) , providing a more convenient way to construct custom ErrorRecords implicitly, via arguments passed to new Cmdlet.WriteError() / Cmdlet.ThrowTerminatingError() overloads, as suggested by @lzybkr, is well worth considering.

All 14 comments

Is there a good reason to not prefer an improved api?
I'd imagine this is annoying for authors of C# cmdlets as well.

Can we use

$a=Write-Error -Message "eee" 2>&1

instead of New-ErrorRecord?

@iSazonov that's a clever way to achieve the same result, but I don't think everyone should need to be that clever to create an error record for throwing.

Maybe we just add such feature in Write-Error ? New parameter?

A new parameter on Write-Error is still not obvious, and still doesn't help C# authors.

I'd start with a new api - and if that's still not sufficient, only then would I consider a new cmdlet.

@lzybkr Can you help me understand as someone without background in C# what the experience would be in Powershell for interacting with a new API? I'm not sure what that would mean in this context. I'm happy for any solution that makes it easier to generate detailed terminating/non-terminating errors.

Is the objection to needing New-Object? Or the need for multiple invocations to New-Object?

It's already fine to throw an ErrorRecord today even though it is not an Exception.

I was thinking there could be another constructor to ErrorRecord with a signature similar to the New-ErrorRecord script you linked to, so you'd use something like:

$err = [ErrorRecord]::new([InvalidOperationException], $message, $errorId, $errorCategory, $targetObj)

It's definitely useful to be able to make a new error record with an exception in a single line, but I'm pretty sure that it would be enough if there was just a -AsTerminatingException switch on Write-Error ;-)

@Jaykul Personally I am fine with that solution. I think that covers 95% of the use cases, I just wasn't sure if throwing a terminating error was kosher for a "Write" verb.

Maybe -Throw more clear for users?

It is not exclude New-ErrorRecord for more complex cases (5%).

Let me try to summarize and make a suggestion:

  • In _PowerShell code_, Write-Error, via its -Category, -ErrorId, ... parameters, already offers a way to create custom ErrorRecord instances, by either wrapping a _custom error message_ (-Message) or a _preexisting exception_ (-Exception) - though the latter is currently broken - see #10774.

    • However, creating such a custom ErrorRecord is currently tied solely to writing directly to the error stream, i.e. limited to creating a _non-terminating_ error.

    • Introducing something like a -AsTerminatingError switch as suggested by @Jaykul (or @iSazonov's simpler alternative name, -Throw, which I like for its association with the Throw statement) would remove this limitation, to allow the custom error record to be thrown as a script-terminating (runspace-terminating) error as well, like a customized Throw call.

      • As an aside: A _script_-terminating error is the only kind of terminating error you can emit from PowerShell code ( unless you use $PSCmdlet.ThrowTerminatingError directly, which scripters should not be expected to need to use); conversely, (binary) cmdlets _cannot_ raise script-terminating errors themselves and can only create _statement_-terminating ones, via Cmdlet.ThrowTerminatingError() or by throwing or not catching a raw exception.
    • Someone who really wants to create a custom ErrorRecord _in isolation_, for later use - which is clearly an advanced use case - can use New-Object System.Management.Automation.ErrorRecord or [System.Management.Automation.ErrorRecord]::new() directly, as needed.

  • _Separately_, in parallel, _for SDK users_ (in C#) , providing a more convenient way to construct custom ErrorRecords implicitly, via arguments passed to new Cmdlet.WriteError() / Cmdlet.ThrowTerminatingError() overloads, as suggested by @lzybkr, is well worth considering.

One point I'd like to mention is that Write-Error _does_ in fact support creating terminating errors, via -ErrorAction Stop if I'm not mistaken. 馃檪

The rest sounds good!

Thanks, @vexx32. -ErrorAction Stop does work, but is suboptimal in that it creates an _extra_ entry in $Error: a System.Management.Automation.ActionPreferenceStopException exception with message
The running command stopped because the preference variable "ErrorActionPreference" or common parameter is set to Stop

Write-Error also doesn't provide access to all the properties of ErrorRecord. Consider ScriptStackTrace for example.

Was this page helpful?
0 / 5 - 0 ratings