Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Running "Get-PACertificate" can cause a long stream of errors #544

Closed
joshooaj opened this issue Apr 28, 2024 · 4 comments
Closed

Running "Get-PACertificate" can cause a long stream of errors #544

joshooaj opened this issue Apr 28, 2024 · 4 comments
Assignees
Labels
bug Something isn't working

Comments

@joshooaj
Copy link
Contributor

On a clean installation of Posh-ACME, running Get-PACertificate results in an error which is totally expected, and the error message makes it clear how to resolve it (nice!). However, there are 7 consecutive errors in PS 5.1 and 4 in PS 7.4.

It seems like $PSCmdlet.ThrowTerminatingError($_) in the catch block of the try/catch's isn't having the intended effect (or I'm misunderstanding PowerShell's funky error system again - wouldn't be the first time).

In the example of Get-PACertificate, I replaced the try/catch/throw with a simple throw, and it seems to work better? What do you think? You obviously chose to use this method of throwing errors for a reason, and it's been a while since I've dug deep into the intricacies of PowerShell error handling.

image

Here's another interesting screenshot where I run 1..4 | % { Write-Warning "Surely this will only run once?"; Get-PACertificate } using the published version of Posh-ACME and the slightly modified version. You would think ThrowTerminatingError() would... terminate... the pipeline. But the ForEach-Object runs 4 times while it runs only once when using a simple throw.

image

@rmbolger
Copy link
Owner

Hey @joshooaj, thanks for the heads up. PowerShell error handling is definitely something I still have a hard time getting "right". If I recall correctly, most of these instances of ThrowTerminatingError used to be just plain throw prior to the 4.0 release because I didn't know any better in the earlier versions. But errors were ugly and pointed to internal logic where it wasn't necessary or relevant for the user.

So I went looking for a better way and wound up on Kevin Marquette's excellent, PowerShell: Everything you wanted to know about exceptions. But clearly, I didn't quite get it right in 4.x. In particular, I apparently missed this:

One nuance of $PSCmdlet.ThrowTerminatingError() is that it creates a terminating error within your Cmdlet but it turns into a non-terminating error after it leaves your Cmdlet. This leaves the burden on the caller of your function to decide how to handle the error. They can turn it back into a terminating error by using -ErrorAction Stop or calling it from within a try{...}catch{...}.

I think it's also the reason your 1..4 test also still runs 4 times instead of 1 even if the module function gets fixed so it only produces a single terminating error. But even adding -EA Stop to the call doesn't seem to halt the loop for me. To get the loop to stop after 1 iteration, I needed to do one of the following:

  • Wrap the whole thing in try/catch
  • Wrap the inner loop contents in a try/catch where the code in the catch broke the loop (throw, ThrowTerminatingError, or return)
  • Change the $ErrorActionPreference variable to Stop instead of the default Continue.

@rmbolger rmbolger self-assigned this Apr 28, 2024
@rmbolger rmbolger added the bug Something isn't working label Apr 28, 2024
@rmbolger
Copy link
Owner

Forgot to add, I'll work on fixing the basic problem with the multiple errors where there should only be one.

@joshooaj
Copy link
Contributor Author

Kevin's blog is such a great resource - I should have known to look there first to refresh my memory on the nuances of error handling!

I suppose it makes sense in some cases to hide inner errors from the user - sort of treating the errors as an "interface" and only exposing what is important. As a developer I'm biased in that I want to see where the actual error is thrown (file and line) and I'm less concerned about where it was re-thrown since that should ultimately be in the stack trace.

I'll have to give that post another read through and think about using ThrowTerminatingError() in my code when exceptions come from private functions so that the error source always looks to be from public functions.

This is a super unimportant issue really, just cosmetic - thanks for taking the time to respond 😎

rmbolger added a commit that referenced this issue May 1, 2024
@rmbolger
Copy link
Owner

rmbolger commented May 4, 2024

Fix is now live in 4.23.0

@rmbolger rmbolger closed this as completed May 4, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants