Try and Catch Me If You Can

In looking at entries in this year’s Scripting Games, as well as posts I see in PowerShell forums, I thought I’d post a short guide to properly using Try/Catch. This is the way I think it should be used. Let’s start with a Try/Catch block that might look ok.


Try {
Get-Service Foo
}

Catch {
Write-Warning "Oops"
Write-Warning $_.Exception.Message
}

You would expect that if the service FOO can’t be found that the code in the Catch script block will run.


PS C:\work> .\trydemo.ps1
Get-Service : Cannot find any service with service name 'Foo'.
At C:\work\trydemo.ps1:4 char:14
+ Get-Service <<<< Foo
+ CategoryInfo : ObjectNotFound: (Foo:String) [Get-Service], Serv
iceCommandException
+ FullyQualifiedErrorId : NoServiceFoundForGivenName,Microsoft.PowerShell.
Commands.GetServiceCommand

Nope. I got an exception, but my code in the Catch script block did not run. You can only catch terminating exceptions. In order for my Try/Catch to work properly, I have to make sure that if there is an exception it gets treated as a terminating exception. The best way is to use the common -ErrorAction parameter and set it to Stop.


Try {
Get-Service Foo -ErrorAction Stop
}

Catch {
Write-Warning "Oops"
Write-Warning $_.Exception.Message
}

The parameter has an alias of -ea which you'll often see used. Now let's see what happens:


PS C:\work> .\trydemo.ps1
WARNING: Oops
WARNING: Cannot find any service with service name 'Foo'.
PS C:\work>

That's what I was expecting. Now for the tricky part. Look at this variation.


Try {
Get-Service -ComputerName Bogus
}

Catch {
Write-Warning "Oops"
Write-Warning $_.Exception.Message
}

What happens when I run this?


PS C:\work> .\trydemo.ps1
WARNING: Oops
WARNING: Cannot open Service Control Manager on computer 'Bogus'. This
operation might require other privileges.

How about that? Even without -erroraction, my catch code still ran. Based on this I can safely deduce that the remote connection failure created a terminating exception. But without a lot of trial and error I think it is impossible to know in advance what type of error will throw a terminating exception and what won't. Personally, I think the best thing is to always use -ErrorAction stop on a cmdlet in a Try scriptblock to guarantee you will always get a terminating exception.

7 thoughts on “Try and Catch Me If You Can”

  1. Jeff –
    For all of your users when they get “tired typing syndrome”. -ea 0

    Good blog – I will keep a link to post for others.

    1. One downside is that it isn’t clear in a script what 0 means. But in the console makes sense.

      1. That seems to be the case. Hardly intuitive which is why I would avoid it in a script.

        PS C:\work> [enum]::GetValues([system.management.automation.actionpreference]) |
        foreach {"$_ = $([int]$_)"}
        SilentlyContinue = 0
        Stop = 1
        Continue = 2
        Inquire = 3

  2. hi,

    you can also use ‘ErrorActionPreference’ and finally block:


    Try {
    $ErrorActionPreference = 'Stop'
    Get-Service Foo
    Get-Process Boo
    }

    Catch {
    Write-Warning "Oops"
    Write-Warning $_.Exception.Message
    }
    finally{
    $ErrorActionPreference = 'SilentlyContinue'
    }

  3. ‘sorry’

    Try {
    $ErrorActionPreference = ‘Stop’
    Get-Service Foo
    Get-Process Boo
    }

    Catch {
    Write-Warning “Oops”
    Write-Warning $_.Exception.Message
    }
    finally{
    $ErrorActionPreference = ‘Continue’
    }

    1. That is certainly an easier way if you are including a number of commands in a single Try scriptblock.

Comments are closed.