Jlisk
Jlisk

Reputation: 333

Powershell catching exception type

Is there a convenient way to catch types of exceptions and inner exceptions for try-catch purposes?

Example code:

$a = 5
$b = Read-Host "Enter number" 
$c = $a / $b #error if $b -eq 0
$d = get-content C:\I\Do\Not\Exist

Row #3 will generate a runtime error with an inner exception (EDIT: fixed this command $Error[1].Exception.InnerException.GetType()), row #4 will generate a "standard"(?) type of exception ($Error[0].Exception.GetType()).

Is it possible to get the desired result from both of these with the same line of code?

Ad1: error from row 3

At -path-:3 char:1

+ $c = $a / $b #error if $b -eq 0
+ ~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException

Ad2: error from row 4

get-content : Cannot find path 'C:\I\Do\Not\Exist' because it does not exist.

At -path-:4 char:6

+ $d = get-content C:\I\Do\Not\Exist

+      ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    + CategoryInfo          : ObjectNotFound: (C:\I\Do\Not\Exist:String) 
[Get-Content], ItemNotFoundException    
    + FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand

Edit: To make it clear, I want the result to return DivideByZeroException and ItemNotFoundException in some way

Upvotes: 13

Views: 19481

Answers (2)

Adam Erickson
Adam Erickson

Reputation: 6363

Just wanted to add that you can create a sorted list of exception types like so:

function Get-Exceptions {
  [OutputType([String[]])]
  [String[]]$Exceptions = [AppDomain]::CurrentDomain.GetAssemblies() | foreach {
    try {
      $_.GetExportedTypes().BaseType | where { $_.Fullname -match 'Exception' }
    } catch {}
  }
  return $Exceptions | Sort-Object -Unique
}

It can be used to find the appropriate exception type like so:

$Exceptions = Get-Exceptions
Write-Output $Exceptions

Upvotes: 0

marsze
marsze

Reputation: 17035

First of all, you can explicitly catch specific exception types:

$ErrorActionPreference = "Stop"

try {
    1 / 0
}
catch [System.DivideByZeroException] {
    $_.Exception.GetType().Name
}

try {
    Get-Item "c:\does-not-exist"
}
catch [System.Management.Automation.ItemNotFoundException] {
    $_.Exception.GetType().Name
}

The DivideByZeroException is basically just the InnerException of RuntimeException, and theoretically, the InnerExceptions could be endlessly nested:

catch {
    $exception = $_.Exception
    do {
        $exception.GetType().Name
        $exception = $exception.InnerException
    } while ($exception)
}

BUT you can handle RuntimeException as a special case. Even PowerShell does so. Look at the first code example. The catch-block is reached even though the type of the inner exception is specified.

You could do something similar yourself:

catch {
    $exception = $_.Exception
    if ($exception -is [System.Management.Automation.RuntimeException] -and $exception.InnerException) {
        $exception = $exception.InnerException
    }
    $exception.GetType().Name
}

NOTE that you need one try-catch per command, if you want to catch both exceptions. Else the 2nd will not be executed if the 1st one fails. Also you have to specify $ErrorActionPreference to "Stop" to catch also non-terminating exceptions.

Upvotes: 13

Related Questions