Wednesday, July 26, 2017

Powershell Try Catch in nested code and how to

I was writing some powershell (quite a lot of powershell actually) today and had to go through the code of someone else.  It's always nice to see how other people are doing stuff.

So I saw this piece of code with try catch.  Although the piece of code worked, I didn't agree with the approach.

Let's see what the approaches are and what's the best way. Comments are highly appreciated.




So the actuall piece of code was something like this

# function bla
function bla(){
    try{
        # this will fail, it's not a real command
        blaaaa
    }catch{

        $ErrorMessage = "Failed to blaaaa.  Error : " + $_.Exception.Message
        throw $ErrorMessage
    }
}

# wrapper function blabla
function blabla(){
    try{
        bla
        bla
    }catch{
        $ErrorMessage = "Failed to blabla.  Error : " + $_.Exception.Message
        throw $ErrorMessage
    }
}

# main code

try{
    blabla
}catch{
    $ErrorMessage = "Failed.  Error : " + $_.Exception.Message
    throw $ErrorMessage
}


What's wrong with this ?
Well, although the error is caught, the original error is harder to trace.  Which makes it harder to debug.

Failed.  Error : Failed to blabla.  Error : Failed to blaaaa.  Error : The term 'blaaaa' is no
t recognized as the name of a cmdlet, function, script file, or operable program. Check the sp
elling of the name, or if a path was included, verify that the path is correct and try again.
At line:30 char:5
+     throw $ErrorMessage
+     ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : OperationStopped: (Failed.  Error ... and try again.:String) [] 
   , RuntimeException
    + FullyQualifiedErrorId : Failed.  Error : Failed to blabla.  Error : Failed to blaaaa.   
   Error : The term 'blaaaa' is not recognized as the name of a cmdlet, function, script fil  
  e, or operable program. Check the spelling of the name, or if a path was included, verify   
  that the path is correct and try again.


This how I like to do it.

  • Catch
  • Write what's wrong and where it went wrong (using the $MyInvocation.MyCommand you can list the function name - optional but handy
  • Re-throw the original exception (just by saying 'throw')


# function bla
function bla(){
    try{
        # this will fail, it's not a real command
        blaaaa
    }catch{
        write-host ("[{0}] Failed to blaaaa" -f $MyInvocation.MyCommand) -ForegroundColor cyan
        throw
    }
}

# wrapper function blabla
function blabla(){
    try{
        bla
        bla
    }catch{
        write-host ("[{0}] Failed to blabla" -f $MyInvocation.MyCommand) -ForegroundColor cyan
        throw
    }
}

# main code

try{
    blabla
}catch{
    write-host "Ultimately failed in blabla" -ForegroundColor cyan

    # if you rethrow again, it will show the actual error
    # if you omit this final error, you will have done a full catch, having a trace but no error
    throw
}

This is the new output
Which gives you a better stacktrace feeling.

[bla] Failed to blaaaa
[blabla] Failed to blabla
Ultimately failed in blabla
blaaaa : The term 'blaaaa' is not recognized as the name of a cmdlet, function, script file, o
r operable program. Check the spelling of the name, or if a path was included, verify that the
 path is correct and try again.
At line:5 char:9
+         blaaaa
+         ~~~~~~
    + CategoryInfo          : ObjectNotFound: (blaaaa:String) [], ParentContainsErrorRecordEx 
   ception
    + FullyQualifiedErrorId : CommandNotFoundException

1 comment :