SOLVED

Exception data disappears?

Copper Contributor

I have a Try..Catch in my code where the script logs into the CIMC of our Cisco UCS servers to get hardware info.

I found something which I cannot explain and need your guidance please. Here's the Catch code:

 

       Catch [System.Exception]
            {
            $_.Exception.pstypenames;
            # Did not connect due to connection issue.
            Write-Host "$StringLabel Error: $($PSItem.ToString())" -ForegroundColor Red
            Switch ($PSItem.Exception.ToString())
                {
                {$_.contains("Unable to connect")} 
                    {
                    Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
                    $Connected = $False
                    $ErrorPropertyValue = "Connection_Not_Available"
                    }
                {$_.contains("Authentication failed")}
                    {
                    Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
                    $Connected = $False
                    $ErrorPropertyValue = "Authentication_Failed"
                    }
                Default
                    {
                    Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
                    $Connected = $False
                    $ErrorPropertyValue = "Unknown_Error"
                    }
                }

 

 

The line "$_.Exception.pstypenames;" shows the following values:

 

System.Exception
System.Object

 

 

The line "Write-Host "$StringLabel Error: $($PSItem.ToString())" -ForegroundColor Red" shows the following:

 

[MyServerName/172.72.24.82] Error: Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.

 

 

This is OK so far- the CIMC portal on this server is not accessible and not pingable and this is the expected result.

The Switch statement evaluates this statement to be true:

 

{$_.contains("Unable to connect")} 

 

 

Again, as expected.

Now this is where my problem starts. This line "Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red" shows a blank value.

 

Does $PSItem have a "shelf-life" where it's contents will fade away if not consumed?

Here's some testing I did at the "Catch [System.Exception]" line:

[DBG]: PS C:\Users\jmilano>> $PSItem
Connect-IMC : Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.
At C:\Users\jmilano\Documents\PowerShell_Scripts\UCS\Get-RackServerInventory.ps1:223 char:30
+ ... IMCHandle = Connect-IMC -Name $RackServerIMCIP -Credential $CurrentCr ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [Connect-Imc], Exception
    + FullyQualifiedErrorId : Cisco.Imc.Cmdlets.ConnectImc
 

[DBG]: PS C:\Users\jmilano>> $PSItem.exception
Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.

[DBG]: PS C:\Users\jmilano>> $PSItem.FullyQualifiedErrorId
Cisco.Imc.Cmdlets.ConnectImc

[DBG]: PS C:\Users\jmilano>> $PSItem.exception.GetBaseException()
Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.

[DBG]: PS C:\Users\jmilano>> $PSItem.exception.GetHashCode()
47918467

[DBG]: PS C:\Users\jmilano>> $PSItem.exception.connectionid

[DBG]: PS C:\Users\jmilano>> $PSItem.exception.errorcategory

[DBG]: PS C:\Users\jmilano>> $PSItem.exception.errorid

[DBG]: PS C:\Users\jmilano>> $PSItem.exception.HResult
-2146233088

[DBG]: PS C:\Users\jmilano>> $PSItem.exception.InnerException

[DBG]: PS C:\Users\jmilano>>

 

Here's some testing I did at the last Write-Host command:

 

[DBG]: PS C:\Users\jmilano>> $PSItem
System.Exception: Connect-Imc: 172.72.24.82 300:Unable to connect to the remote server.

[DBG]: PS C:\Users\jmilano>> $PSitem.exception

[DBG]: PS C:\Users\jmilano>> $PSItem.FullyQualifiedErrorId

[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.GetBaseException()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $PSItem.Exception.GetBaseException()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 

[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.GetHashCode()
You cannot call a method on a null-valued expression.
At line:1 char:1
+ $PSItem.Exception.GetHashCode()
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull
 

[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.ConnectionId

[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.ErrorCategory

[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.ErrorId

[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.HResult

[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.InnerException

[DBG]: PS C:\Users\jmilano>> $PSItem.Exception.Message

[DBG]: PS C:\Users\jmilano>> 

 

 

 

 

 

It seems that the$PSItem loses some or all of its properties as soon as the Switch command is executed. Is this correct? Do I have to save the $PSItem properties as soon as the Catch is executed?

2 Replies
best response confirmed by JulianMilano (Copper Contributor)
Solution

@JulianMilano 

 

Hi, Julian.

 

I've used the following code to reproduce your symptoms, after which I get into the issues.

 

Test code

#region Test session-specific stuff. Remove for production use.
Clear-Host;
Remove-Variable -Name @("StringLabel", "Connected", "ErrorPropertyValue") -ErrorAction:SilentlyContinue;
$StringLabel = "Foo";
#endregion

try
{
    dir B:\ -ErrorAction:Stop;
}
Catch [System.Exception]
{
    $_.Exception.pstypenames;
    # Did not connect due to connection issue.
    Write-Host "$StringLabel Error: $($PSItem.ToString())" -ForegroundColor Red
    Switch ($PSItem.Exception.ToString())
    {
        {$_.contains("Unable to connect")} 
            {
                Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Connection_Not_Available"
            }
        {$_.contains("Authentication failed")}
            {
                Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Authentication_Failed"
            }
        Default
            {
                Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Unknown_Error"
            }
    }
}

 

Exception output

LainRobertson_0-1692142871685.png

 

There's two errors in this output:

 

  1. The one from the try block;
  2. A new one thrown from within the catch block.

 

Analysis

Firstly, $_ and $PSItem are precisely the same thing (the former being an alias of the latter), so you can get away with using just one or the other in code.

 

Next, there is indeed a "shelf life" - for multiple reasons:

 

  1. Something else comes onto the pipeline from a subsequent command;
  2. The scope changes.

 

What you're seeing is the effect of the latter, where the scope changes from the catch block to the switch statement/block (which itself is a subset of the broader catch block).

 

Outside of the switch block, $_ still equals your exception, but inside of the switch block, it equals the string emitted by the switch expression of:

 

$PSItem.Exception.ToString()

 

This explains the second error being thrown from the catch block, as a System.String object clearly doesn't have a property named "Exception" (and therefore no ToString() method below that, either), which is reflected in that second error message.

 

The best thing to do when working with the pipeline automatic variable in a complex block (irrespective of whether it's a catch block or any other) is to assign it to a variable upon entry to the block - unless you're not using nested references (you don't want to make things messy if you don't have to.)

 

The variable is usable from anywhere inside of that specific block, independent of the pipeline variable changing for whatever reason.

 

Taking this simple step of assigning the pipeline variable upon entry gives you this instead:

 

Adjusted code

#region Test session-specific stuff. Remove for production use.
Clear-Host;
Remove-Variable -Name @("StringLabel", "Connected", "ErrorPropertyValue") -ErrorAction:SilentlyContinue;
$StringLabel = "Foo";
#endregion

try
{
    dir B:\ -ErrorAction:Stop;
}
Catch [System.Exception]
{
    $e = $_;
    # $e.Exception.pstypenames;
    # Did not connect due to connection issue.

    Switch ($e.Exception.ToString())
    {
        {$e.Exception.Message.Contains("Unable to connect")} 
            {
                Write-Host "$StringLabel ERROR: [$($e.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Connection_Not_Available"
            }
        {$e.Exception.Message.Contains("Authentication failed")}
            {
                Write-Host "$StringLabel ERROR: [$($e.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Authentication_Failed"
            }
        Default
            {
                Write-Host "$StringLabel ERROR: [$($e.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Unknown_Error"
            }
    }
}

$Connected;
$ErrorPropertyValue;

 

Output

LainRobertson_1-1692143347567.png

 

Which is probably what you wanted.

 

If you want to get into the nuts and bolts of the topics this post touches on, here you go:

 

 

Cheers,

Lain

Yes- Scope of variables. Thank you!! I guess $PSItem / $_ are special dynamic variables used by the system and thus can change with each block of code executed. I have indeed saved the $PSItem values to variables as soon as the Catch block is executed and this works as you suggested. Thanks again!!
1 best response

Accepted Solutions
best response confirmed by JulianMilano (Copper Contributor)
Solution

@JulianMilano 

 

Hi, Julian.

 

I've used the following code to reproduce your symptoms, after which I get into the issues.

 

Test code

#region Test session-specific stuff. Remove for production use.
Clear-Host;
Remove-Variable -Name @("StringLabel", "Connected", "ErrorPropertyValue") -ErrorAction:SilentlyContinue;
$StringLabel = "Foo";
#endregion

try
{
    dir B:\ -ErrorAction:Stop;
}
Catch [System.Exception]
{
    $_.Exception.pstypenames;
    # Did not connect due to connection issue.
    Write-Host "$StringLabel Error: $($PSItem.ToString())" -ForegroundColor Red
    Switch ($PSItem.Exception.ToString())
    {
        {$_.contains("Unable to connect")} 
            {
                Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Connection_Not_Available"
            }
        {$_.contains("Authentication failed")}
            {
                Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Authentication_Failed"
            }
        Default
            {
                Write-Host "$StringLabel ERROR: [$($PSItem.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Unknown_Error"
            }
    }
}

 

Exception output

LainRobertson_0-1692142871685.png

 

There's two errors in this output:

 

  1. The one from the try block;
  2. A new one thrown from within the catch block.

 

Analysis

Firstly, $_ and $PSItem are precisely the same thing (the former being an alias of the latter), so you can get away with using just one or the other in code.

 

Next, there is indeed a "shelf life" - for multiple reasons:

 

  1. Something else comes onto the pipeline from a subsequent command;
  2. The scope changes.

 

What you're seeing is the effect of the latter, where the scope changes from the catch block to the switch statement/block (which itself is a subset of the broader catch block).

 

Outside of the switch block, $_ still equals your exception, but inside of the switch block, it equals the string emitted by the switch expression of:

 

$PSItem.Exception.ToString()

 

This explains the second error being thrown from the catch block, as a System.String object clearly doesn't have a property named "Exception" (and therefore no ToString() method below that, either), which is reflected in that second error message.

 

The best thing to do when working with the pipeline automatic variable in a complex block (irrespective of whether it's a catch block or any other) is to assign it to a variable upon entry to the block - unless you're not using nested references (you don't want to make things messy if you don't have to.)

 

The variable is usable from anywhere inside of that specific block, independent of the pipeline variable changing for whatever reason.

 

Taking this simple step of assigning the pipeline variable upon entry gives you this instead:

 

Adjusted code

#region Test session-specific stuff. Remove for production use.
Clear-Host;
Remove-Variable -Name @("StringLabel", "Connected", "ErrorPropertyValue") -ErrorAction:SilentlyContinue;
$StringLabel = "Foo";
#endregion

try
{
    dir B:\ -ErrorAction:Stop;
}
Catch [System.Exception]
{
    $e = $_;
    # $e.Exception.pstypenames;
    # Did not connect due to connection issue.

    Switch ($e.Exception.ToString())
    {
        {$e.Exception.Message.Contains("Unable to connect")} 
            {
                Write-Host "$StringLabel ERROR: [$($e.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Connection_Not_Available"
            }
        {$e.Exception.Message.Contains("Authentication failed")}
            {
                Write-Host "$StringLabel ERROR: [$($e.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Authentication_Failed"
            }
        Default
            {
                Write-Host "$StringLabel ERROR: [$($e.Exception.ToString())]." -ForegroundColor Red
                $Connected = $False
                $ErrorPropertyValue = "Unknown_Error"
            }
    }
}

$Connected;
$ErrorPropertyValue;

 

Output

LainRobertson_1-1692143347567.png

 

Which is probably what you wanted.

 

If you want to get into the nuts and bolts of the topics this post touches on, here you go:

 

 

Cheers,

Lain

View solution in original post