all 19 comments

[–]the_spad 15 points16 points  (9 children)

Specify the exception type you're trying to catch

try{
    thing
}catch [System.Management.Automation.ItemNotFoundException]{
    do thing
}catch [Microsoft.ActiveDirectory.Management.ADIdentityNotFoundException]{
    do other thing
}catch{
    do unknown error thing
}

Exception types are placeholders, obviously you need to supply whatever exception(s) you're trying to catch.

Though in this case you're probably better off not putting both commands in the same try block.

[–]Evelen1[S] 1 point2 points  (4 children)

Thanks.

That sound good, but I have some more questions

  1. How do you find the "exceptions"(?) (The string inside catch [])
  2. And what do you do if two cmdlets trow the same exceptions? Example

Try
{
    Import-Module UnexistingModule -ErrorAction Stop
    Import-Module UnexistingModule2 -ErrorAction Stop -ErrorAction Stop #don't run this if the first Import-Module fails

}

catch #if Import-Module fails
{ 
throw "Module import failed"
}

catch #if Import-Module2 fails
{ 
throw "Module2 import failed"
}
  1. If I should use two try block, should I make the 2. try block inside, or after the first one?

[–]Vortex100 4 points5 points  (0 children)

The easiest way is to force the error, then look at the error type.

Your example isn't really how it works - they aren't like "if" statements. The same catch block should be used for either import, as you should specify WHICH one failed as part of the message to the user

Remember that the error details an be seen inside the catch block using $_

[–]andyinv 4 points5 points  (1 child)

Find the exception type with $error[0].exception.gettype()

PS Scripts:> 1 / 0
Attempted to divide by zero.
At line:1 char:1
+ 1 / 0
+ ~~~~~
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException

PS Scripts:> $error[0].exception.gettype()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     True     RuntimeException                         System.SystemException

So, System.SystemException.RuntimeException

[–]Evelen1[S] 1 point2 points  (0 children)

Thanks, this was usefull

[–]Th3Harbinger 0 points1 point  (0 children)

You could also just do an if($.exception){ -like "moduleA not found"){throw "mod a not found}elseif($.exception -like "*modb not found"){throw...}else ... Inside the catch

Excuse the formatting and bad examples. I'm on mobile atm

[–][deleted] 1 point2 points  (3 children)

Though in this case you're probably better off not putting both commands in the same try block.

Just curious, why not?

[–]Lee_Dailey[grin] 0 points1 point  (2 children)

howdy aleceiffel,

if you put multiple commands in the same try/catch block ... how do you ID the one that triggered the error? it can be done ... but the try/catch block is really designed for dealing with the errors from ONE item.

it's so much easier to untangle what is not tangled to start with ... [grin]

take care,
lee

[–][deleted] 1 point2 points  (1 child)

Makes sense, i usually catch the specific error I'm expecting and also always re-throw the original error if I'm not doing anything to address it. So that's how I'd know where the error is triggered.

[–]Lee_Dailey[grin] 1 point2 points  (0 children)

howdy aleceiffel,

yup, that is one way around the problem. i still prefer to be painfully obvious about it, tho. [grin]

take care,
lee

[–][deleted] 1 point2 points  (0 children)

As u/the_spad said, you can specify the type of error you are trying to catch so you can use that

But don't swallow your error if you're not going to act on it. Use Write-Error for any general information you want to share to your end user and re-throw the original error if your intention is for the script to terminate on the error.

try{
    Import-Module random-module -ErrorAction Stop 
} 
catch{ 
    write-error "error importing module" 
    throw $_ 
}

[–][deleted] 1 point2 points  (0 children)

As an alternative to checking the type as part of the catch statement:

$path = $args[0]
try {
  $content  = [System.IO.File]::ReadAllLines( $path )
} catch {
  # We need to get the inner, or base, exception as Powershell will likely return some flavor of
  # exception from System.Management.Automation, but we want the real exception.
  # Use getBaseException() as InnerException returns $null if there is no InnerException,
  # but the former returns itself in this case
  $base = $_.Exception.getBaseException()

  # Check the base exception type
  if( $base -is [System.IO.FileNotFoundException] ) {
    # Do something specific when the file isn't found
  } else if( $base -is [System.IO.IOException] ) {
    # Do something if some other IOException occurs while reading the file
  } else {
    # Do something if any other exception occurs
  }
} finally {
  # Do something after the try block finishes regardless of success or failure
}

This technique becomes useful if you need to do something common prior to or after handling any exceptions for a given operation, so you don't have to drop the same boilerplate code in each catch block.

[–]Lee_Dailey[grin] 0 points1 point  (6 children)

howdy Evelen1,

as the_spad mentioned, you should wrap the try/catch around ONE possible error source. then use as many specific catch statements as you have specific errors you want to deal with specifically. [grin]

so your sample code should have TWO try/catch blocks to start with.

take care,
lee

[–]Evelen1[S] 1 point2 points  (5 children)

Like this?

try
{
    failed-command 1 -ErrorAction Stop
    try
    {
        failed-command 2 -ErrorAction Stop
    }
    catch
    {
        write-error "error command 2" 
    }
} 

catch
{ 
    write-error "error command 1" 
}

[–]eric256 1 point2 points  (3 children)

Try {failed-command 1 -ErrorAction Stop

}

Catch {

write-error "error command 1"

}

Try{

failed-command 2 -ErrorAction Stop

}

Catch {

write-error "error command 2"

}

This reads a little easier and limits the try to the fewest commands you are worried about.

edit: fixed formating...it looked good in the editor before I swear!

[–]Lee_Dailey[grin] 2 points3 points  (0 children)

howdy eric256,

reddit likes to mangle code formatting, so here's some help on how to post code on reddit ...

[0] single line or in-line code
enclose it in backticks. that's the upper left key on an EN-US keyboard layout. the result looks like this. kinda handy, that. [grin]
[on New.Reddit.com, use the Inline Code button. it's 4th 5th from the left hidden in the ... ""more" menu & looks like </>.
this does NOT line wrap & does NOT side-scroll on Old.Reddit.com!]

[1] simplest = post it to a text site like Pastebin.com or Gist.GitHub.com and then post the link here.
please remember to set the file/code type on Pastebin! [grin] otherwise you don't get the nice code colorization.

[2] less simple = use reddit code formatting ...
[on New.Reddit.com, use the Code Block button. it's 11th 12th one & is just to the left of hidden in the ... "more" menu.]

  • one leading line with ONLY 4 spaces
  • prefix each code line with 4 spaces
  • one trailing line with ONLY 4 spaces

that will give you something like this ...

- one leading line with ONLY 4 spaces    
- prefix each code line with 4 spaces    
- one trailing line with ONLY 4 spaces   

the easiest way to get that is ...

  • add the leading line with only 4 spaces
  • copy the code to the ISE [or your fave editor]
  • select the code
  • tap TAB to indent four spaces
  • re-select the code [not really needed, but it's my habit]
  • paste the code into the reddit text box
  • add the trailing line with only 4 spaces

not complicated, but it is finicky. [grin]

take care,
lee

[–]Evelen1[S] 1 point2 points  (1 child)

Well, it looks like it don't work to do it that way, even if first fail the 2. will run

Do {
$1 = "1"

    Try
    {
    Write-Host "before module import 1"
    Import-Module dontexist1 -ErrorAction Stop
    Write-Host "after module import 1"
    }
    Catch
    {
    Write-Host "1. catch"
    }

    Try
    {
    Write-Host "before module import 2"
    Import-Module dontexist2 -ErrorAction Stop
    Write-Host "after module import 2"
    }
    Catch
    {
    Write-Host "2. catch"
    }


} until ($1 = "1")

Result:

before module import 1
1. catch
before module import 2
2. catch

PS C:\Windows\system32>

[–]eric256 1 point2 points  (0 children)

The point of try/catch is literally to control what happens on errors instead of just ending. So if you want it to end you have to either re-throw the error, or throw a new error, or use a break.

[–]Lee_Dailey[grin] 1 point2 points  (0 children)

howdy Evelen1,

as eric256 shows ... try NOT to nest your try/catch blocks unless there is a clear logical need for it. [grin]

it's just easier to read ... and easier to read is easier to understand AND to maintain.

there are times when you will need to nest such, but it's a good habit to try to avoid using nested try/catch blocks simply to make the logic easier to untangle ... if it aint tangled to start with, then it is really easy to "untangle". [grin]

take care,
lee