Exceptions and Error Handling (PowerShell)

From vwiki
Revision as of 16:37, 17 April 2013 by Sstrutt (talk | contribs) (→‎Basic Error Handler: Typo fix)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Behaviour on Exception

To control how a script behaves as a result of an exception, modify the $ErrorActionPreference variable, if required.

In the default continue mode, an error will output to the screen, then the script will continue. By outputting the error to the screen PowerShell CmdLets consider the exception to have been handled! If you want the opportunity to catch or trap you may need to append -ErrorAction:Stop to the CmdLet you expect might fail. However this doesn't appear to be consistent, and some trial and error is to be expected.

Value Effect
Continue [Default] Outputs error, but keeps processing
SilentlyContinue No output and it keeps going
Inquire Prompt user for action
Stop Outputs error and halts processing

$error

Provides a list of recent errors experienced - which can be invaluable for properly identifying and investigating errors. The object is the same as found in a pipeline when an exception has occurred.

Property Description Example (VC login error)
$error[1].Exception.GetType().FullName Error class for specific error [VMware.VimAutomation.ViCore.Types.V1.ErrorHandling.InvalidLogin]
$error[1].Exception.Message User friendly error message 16/11/2010 10:32:52 Connect-VIServer Login failed due to a bad username or password.
$error[1].ErrorDetails.Message Shorter friendly error message Login failed due to a bad username or password
$error[1].CategoryInfo.Reason Short error message InvalidLogin
$error[1].InvocationInfo Invocation info (what triggered exception) Various info including script command

Basic Error Handler

If you know where the error is likely to occur, then just place an error catcher immediately after it. This doesn't stop the exception appearing on the console, but does allow you to take some action as a result.

if (-not $?) {
    # Handle error here
  }

Try...Catch

Used to catch an exception in a script block where an exception may be likely. Stops the exception being shown on the console and gives you a chance to do something about it (as long as you've set $ErrorActionPreference to Stop or used -ErrorAction:Stop).

try
{
    # Something in which an exception is likely
}
catch
{
    Write-Host "FAILED: $_"
    Exit
}

The error returned by the CmdLet can be found in $_ , so this can be tested to ensure the error is as expected (just because you though a command might fail, doesn't mean it failed in the way you expected). For example, $_ will contain the bold bit of the following error.

Get-Log : Cannot validate argument on parameter 'StartLineNum'. The 0 argument is less than the minimum allowed range of 1. Supply an argument that is greater than 1 and then try the command again.
At C:\Users\simonstrutt\Documents\Scripts\ESX-LogTail.ps1:20 char:57
+     $ESXLog = Get-Log $logKey -VMHost $ESX -StartLineNum <<<<  $LineNo
    + CategoryInfo          : InvalidData: (:) [Get-Log], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,VMware.VimAutomation.Commands.GetLog 

This can be elaborated on to make the catch handling more specific, by making the catch block executed depend on the error class. The error class can be determined by making causing the error to be thrown, in which case the class can be found at $_.Exception.GetType().FullName .

try {
   $proxy = New-WebServiceProxy -uri $endpoint -cred $credential -ErrorAction:Stop
} catch [System.Net.WebException] {
    Write-Host "ERROR: Unable to connect to SOAP server"
    Write-Host $_
} catch {
    Write-Host "ERROR: Unexpected error"
    Write-Host $_
    Write-Host $_.Exception.Message
    Write-Host $_.Exception.GetType().FullName
}

Trap

Used to capture any unhanded exception that occurs anywhere. I tend to consider this a last resort catch-all, though really it depends on the nature of your script. For example if your script is reliant on a connection to a server that can go down, you can design a trap to recover from that specific occurrence rather than having to put a Try...Catch around every operation that could fail.

The key to an effective trap is allowing for the fact that anything might go wrong, therefore you have to set your traps up to handle only specific cases and in all likelihood stop on anything else.

trap {
    # Handle the exception
    Continue
}

Sources / Further Reading