PowerShell try/catch Strangeness and Terminating Errors

For my sins I have been (and to some extents still am) a C# developer. Compared with scripting there is a lot I like about writing applications in fully fledged high-level languages, especially when it comes to flow control.

Not all scripting languages are created equal and something I really like about PowerShell is its support for .NET style try-catch-finally statements. Error handling in PowerShell v1 was pretty rubbish – anyone remember trying to figure out the trap statement? – but it’s now very good and gives a lot of control over how errors are caught and handled.

Essentially there are 2 types of errors in PowerShell – terminating and non-terminating. As you might expect given the name, if a non-terminating error occurs then statement execution still continues. This makes sense for many sysadmin operations where you don’t want one failure to cause your scripts to stop (such as a server being offline). Terminating errors cause statement execution to stop and PowerShell looks for an error handler instead (such as a catch statement). If none is found then the script will exit.

But something weird I noticed is that surrounding a command with a try-catch statement can make PowerShell change a non-terminating error into a terminating one.

This occurs when an exception is thrown from a .NET class. To show what happens, let’s use a very basic dummy class with a single static method that throws an exception:

$Definition = "public class Foo { public static void Bar() " `
            +" { throw new System.Exception(""Exception""); } }"
Add-Type -TypeDefinition $Definition -Language CSharp

Calling Bar() outside of try-catch will generate a non-terminating error. You can see that PowerShell continues execution as the “After” string is sent to the console. (It is important here that $ErrorActionPreference is Continue – if it is set to Stop then PS will treat all errors as terminating).

PS C:\Users\Administrator> $ErrorActionPreference
PS C:\Users\Administrator> "Before" ; [Foo]::Bar(); "After"
Exception calling "Bar" with "0" argument(s): "BarException"
At line:1 char:12
+ "Before" ; [Foo]::Bar(); "After"
+            ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : Exception

But put a try-catch around the call to Bar() and PowerShell changes the behaviour to a terminating error, and control is sent to the catch block instead of “After” being printed:

PS C:\Users\Administrator> try { "Before" ; [Foo]::Bar(); "After" } catch {"Exception" }

I don’t know why this happens but it’s important to know. If you add try-catch error handling to your PowerShell scripts – something I recommend as it makes your scripts more readable and maintainable – then you might end up changing their behaviour in ways you don’t expect. So be sure to test, test and test again!

This entry was posted in PowerShell, Scripting. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *