UPDATE May 2021 - When I originally asked this question, the core thing that made this question relevant (for me) was that when rethrowing an exception from a catch via a simple throw
(by itself), the original exception stack was lost. So that made using a catch to detect if an exception was thrown off-limits.
This incorrect loss-of-stack behavior was fixed sometime between when the question was asked (2017) and now. So a simple catch and rethrow (call throw
with no other arguments) is now the most straightforward way to detect an exception was thrown from the finally
block. Thanks to @JohnLBevan for his answer letting me know that rethrowing from the catch was no longer problematic.
ORIGINAL QUESTION:
I've got some code structured like this
try{
...
}
finally{
...
<code that may throw>
}
Of course one should generally avoid code that throws in a finally. But it can happen. And when it does, one unfortunate side effect is that the original exception is lost. So the first thing I'd like to do in the finally is log information about the exception thrown in the try, if one was thrown.
But how can I determine if an exception did occur in the try block, once I'm in the finally? Is there a slick way? I don't want to catch the exception in a catch. I can set a boolean at the end of the try which would indicate an exception was not thrown, but I'm not a big fan of having to do that every time. That would look like this:
$exceptionThrown = $true
try{
...
$exceptionThrown = $false
}
finally{
<if $exceptionThrown log info about it>
...
<code that may throw>
}
Can I do better?
If the only reason you're avoiding the catch block is because you don't want to affect the stack trace, you can use it then rethrow the error with the original line number by using throw
with no arguments; thus rethrowing the original exactly as if you'd not used the catch block. For example:
$exceptionInfo = $null
try {
1/0 # cause some error
} catch {
$exceptionInfo = $_.Exception # you could set a flag / whatever here; without knowing your requirement I can't advise further
throw # you said you didn't want to catch it... but if you just don't want to impact the stack trace this is fine as we're rethrowing the original exception; not throwing a new one
} finally {
if ($null -eq $exceptionInfo) {
Write-Information 'Completed Successfully' -InformationAction Continue
} else {
Write-Warning "An error occurred $exceptionInfo"
}
}
If you don't want to use a catch block and don't want to use some variable you've defined to flag whether an exception's occurred, you could use $Error
; though you may need to clear it first as it will contain all errors which have been raised in the current session...
$Error.Clear()
try {
1/0
} finally {
if ($Error.Count) {
Write-Warning "An error occurred $($Error[0])"
} else {
Write-Information 'Completed Successfully' -InformationAction Continue
}
}
Generally you shouldn't need to determine whether something was successful in a finally block though; rather:
throw
if you want to rethrow the original exception so that it still bubbles up afterwards).Below's a rough illustration; the actual example's a bit poor as I couldn't think of a good & succinct real world example scenario; but hopefully you get the idea.
try {
$con = Get-MyDbConnection
New-DbRecord -Connection $con -Data $data
} catch {
Write-Log $_.Exception
} finally {
if (($null -ne $con) -and ($con.IsConnected)) {
$con.Disconnect()
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With