Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine if an exception was thrown from try block when you execute the finally

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?

like image 699
aggieNick02 Avatar asked Oct 18 '25 06:10

aggieNick02


1 Answers

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:

  • If you have logic that you only want to occur if the command is successful, place it in the TRY block after the line that may cause an exception.
  • If you have logic that you only want to occur if the command hits an error, place it in the catch block (and use throw if you want to rethrow the original exception so that it still bubbles up afterwards).
  • If you have logic that should run in either case, that's where the finally block comes in. Sometimes you may need to know the state of some component here (e.g. is a connection to your database still open) and that state may have changed due to the exception... If that's the case, normally such components should provide their own flags.

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()
    }
}
like image 51
JohnLBevan Avatar answered Oct 20 '25 09:10

JohnLBevan



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!