locked
Using an Error Variable strips Error properties still present in $Error (inconsistant from one PS cmd to another) RRS feed

  • Question

  • Hi i am hoping someone with a good grasp of PowerShell Error Variables can help me. This issue is driving me crazy.

    The background is, due to the requirements of a 3rd party program, i am trying to manipulate PowerShell error display for both $error and custom error variables. Basically I need to display the line number and exception in a single line only.

    I have written a function to do this and it works fine using the input of the $error variable (containing all script errors). I have used the following properties to get this information within the function:

    $error.InvocationInfo.ScriptLineNumber
    $error.exception.Message

    My issue is that if the error of a PS cmd is piped to an Error Variable using the -ev switch, the process seems to strip out some these error properties in the Error Variable, even though all properties are still in the $error details. Scriptlinenumber is completely removed and the exception path changes to 'exception.message'. Therefore I can't run the function on the Error Variable reliably.  I do not understand why this happens on some commands and not others or at all - I want to stop the stripping of these properties happening. Can anyone help me?

    The code below should allow anyone to recreate this issue:

    #I Have found that piping to an error variable sometimes does not contain all the error information #even though all the properties are in the $error variable #to recreate: $error.Clear() $erruser = $null #A Cmd i know will fail and generate an error get-aduser "mitch" -ea SilentlyContinue -ErrorVariable erruser "ERROR PROPERTIES" $Error | Format-List -Force "ERRUSER PROPERTIES" $erruser | Format-List -Force "ERROR TEST" $error.InvocationInfo.ScriptLineNumber $error.exception.Message "----.message" $error.Message "ERRUSER TEST"

    #These properties no longer exist? Why not? $erruser.InvocationInfo.ScriptLineNumber $erruser.exception.Message "----.message" $erruser.Message





    Tuesday, June 12, 2018 1:01 PM

Answers

  • You can unbury the info like this:

    $erruser[0].ErrorRecord.InvocationInfo


    \_(ツ)_/

    • Marked as answer by PeteMitch99 Tuesday, June 12, 2018 4:53 PM
    Tuesday, June 12, 2018 3:41 PM
  • I suspect that you can check the type and choose the unbundling method.

    if($errtc[0].GetType().Name -eq 'ErrorRecord'){
          $errtc[0].InvocationInfo 
    }else{
         $errtc[0].ErrorRecord.InvocationInfo
    }

    Some CmdLets from third parties may throw native exceptions which may not be handled in this way.


    \_(ツ)_/




    • Edited by jrv Tuesday, June 12, 2018 3:50 PM
    • Marked as answer by PeteMitch99 Tuesday, June 12, 2018 4:53 PM
    Tuesday, June 12, 2018 3:47 PM
  • I have updated my script with this bit of code to make the locations consistent.

    #If the individual error gettype equals 'ErrorRecord' then set correct path for Errorpath properties, if not set path for CmdletInvocationException if($OrgErr.GetType().Name -eq 'ErrorRecord'){ #for- TypeName: System.Management.Automation.ErrorRecord $ErrInvPath = $OrgErr.InvocationInfo $ErrCatPath = $OrgErr.categoryinfo $ErrExcPath = $OrgErr.exception }else{ #TypeName: System.Management.Automation.CmdletInvocationException (or any other exception) $ErrInvPath = $OrgErr.ErrorRecord.InvocationInfo $ErrCatPath = $OrgErr.ErrorRecord.categoryinfo $ErrExcPath = $OrgErr}

    #Calls $ErrInvPath.line #psline code $ErrInvPath.pscommandpath #path $ErrInvPath.ScriptLineNumber #linenum $ErrInvPath.OffsetInLine #offset $ErrCatPath.Activity #pstool $ErrCatPath.Reason #reason $ErrExcPath.message #exception message








    • Edited by PeteMitch99 Thursday, June 28, 2018 12:38 PM
    • Marked as answer by PeteMitch99 Thursday, June 28, 2018 12:39 PM
    Thursday, June 28, 2018 12:29 PM

All replies

  • I don't understand exactly what you try but if you like to keep some of the automaticly created error variables from Powershell you can assign their "content" to variables created by yourself. These will not be overwritten from Powershell.  ;-)

    Best regards,

    (79,108,97,102|%{[char]$_})-join''

    Tuesday, June 12, 2018 1:38 PM
  • Thanks for your reply. I need to do it using -errorvariable though. Or at least understand why it does not work.

    Tuesday, June 12, 2018 2:46 PM
  •  I need to do it using -errorvariable though.

    I did not recommend not to use -errorvariable. I meant to save the content of -ErrorVariable in self created variables before they will be overwritten by the next cmdlet ...

    Best regards,

    (79,108,97,102|%{[char]$_})-join''

    Tuesday, June 12, 2018 2:52 PM
  •  I need to do it using -errorvariable though.

    I did not recommend not to use -errorvariable. I meant to save the content of -ErrorVariable in self created variables before they will be overwritten by the next cmdlet ...

    Best regards,

    (79,108,97,102|%{[char]$_})-join''

    "ErrorVariable" assigns a local variable to get a copy of the errors.  BY using "+" the variable is cumulative.  The PowerShell "$error" adds a wrapper to the error with line number and other information.  This information is not available in the local variable.

    <cmdlet> … -ErrorVariable +myerrors

    This makes the variable cumulative.

    The variable contains all necessary but does not include the "invocation" information.   You can use a different variable for each command.


    \_(ツ)_/


    • Edited by jrv Tuesday, June 12, 2018 3:01 PM
    • Proposed as answer by BOfH-666 Tuesday, June 12, 2018 3:03 PM
    • Unproposed as answer by PeteMitch99 Tuesday, June 12, 2018 3:25 PM
    Tuesday, June 12, 2018 2:58 PM
  • Sadly. This doesn't answer my question. i don't have a problem with overwriting -errorvariable variables. I have a issue with the invocation information being stripped off. I don't understand why this happens (only in some cases) or how to prevent it.
    Tuesday, June 12, 2018 3:23 PM
  • Sadly. This doesn't answer my question. i don't have a problem with overwriting -errorvariable variables. I have a issue with the invocation information being stripped off. I don't understand why this happens (only in some cases) or how to prevent it.

    It is not stripped off.  he local variable does not contain this info.  As I noted.  This is added by PowerShell when the error is added to the global $error variable.


    \_(ツ)_/

    Tuesday, June 12, 2018 3:26 PM
  • If the properties are not in the content of the error variable what good would copying the error variable do?
    Tuesday, June 12, 2018 3:29 PM
  • If the properties are not in the content of the error variable what good would copying the error variable do?

    There is no need to copy the custom error variable.  I think what was meant was to copy the $error variable which still doesn't make much sense.

    Some Commands may produce full  variables but the line number will always be "1".  Other commands will produce raw error objects.  You can get some extra info by detecting the object type.

    https://docs.microsoft.com/en-us/dotnet/api/system.management.automation.errorrecord?view=powershellsdk-1.1.0


    \_(ツ)_/

    Tuesday, June 12, 2018 3:35 PM
  • That sounds plausible and i would accept if the behavior was consistent. However how does this explain this working as expected - the only real difference is the PS command used...:

    #See - https://social.technet.microsoft.com/Forums/windowsserver/en-US/60bb7de7-8ed1-4e5e-a7ac-78a3e91b2d41/piping-to-an-error-variable-strips-error-properties-inconsistant-from-one-ps-cmd-to-another?forum=winserverpowershell#60bb7de7-8ed1-4e5e-a7ac-78a3e91b2d41
    
    #I Have found that piping to an error varible sometimes does not contain all the error information
    #even though all the properties are in the $error variable
    #to recreate:
    
    $error.Clear()
    $erruser = $null
    
    #A Cmd i know will fail and generate an error
    Test-Connection "test1" -Count 1 -ea SilentlyContinue -ErrorVariable errtc
    
    "ERROR PROPERTIES"
    $Error | Format-List -Force
    "ERRUSER PROPERTIES"
    $errtc | Format-List -Force
    
    "ERROR TEST"
    $error.InvocationInfo.ScriptLineNumber
    $error.exception.Message
    "----.message"
    $error.Message
    
    "ERRUSER TEST"
    $errtc.InvocationInfo.ScriptLineNumber
    $errtc.exception.Message
    "----.message"
    $errtc.Message


    Tuesday, June 12, 2018 3:35 PM
  • Consider the following:

    PS C:\scripts> get-aduser "mitch" -ErrorVariable erruser
    get-aduser : Cannot find an object with identity: 'mitch' under: 'DC=KAHLNET,DC=local'.
    At line:1 char:1
    + get-aduser "mitch" -ErrorVariable erruser
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : ObjectNotFound: (mitch:ADUser) [Get-ADUser], ADIdentityNotFoundException
        + FullyQualifiedErrorId : Cannot find an object with identity: 'mitch' under: 'DC=KAHLNET,DC=local'.,Microsoft.ActiveDirectory.Management.Commands.GetADUser
    
    PS C:\scripts> $erruser|gm
    
    
       TypeName: System.Management.Automation.CmdletInvocationException
    
    Name                        MemberType Definition
    ----                        ---------- ----------
    Equals                      Method     bool Equals(System.Object obj), bool _Exception.Equals(System.Object obj)
    GetBaseException            Method     System.Exception GetBaseException(), System.Exception _Exception.GetBaseException()
    GetHashCode                 Method     int GetHashCode(), int _Exception.GetHashCode()
    GetObjectData               Method     void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context)...
    GetType                     Method     type GetType(), type _Exception.GetType()
    ToString                    Method     string ToString(), string _Exception.ToString()
    Data                        Property   System.Collections.IDictionary Data {get;}
    ErrorRecord                 Property   System.Management.Automation.ErrorRecord ErrorRecord {get;}
    HelpLink                    Property   string HelpLink {get;set;}
    HResult                     Property   int HResult {get;set;}
    InnerException              Property   System.Exception InnerException {get;}
    Message                     Property   string Message {get;}
    Source                      Property   string Source {get;set;}
    StackTrace                  Property   string StackTrace {get;}
    TargetSite                  Property   System.Reflection.MethodBase TargetSite {get;}
    WasThrownFromThrowStatement Property   bool WasThrownFromThrowStatement {get;set;}
    
    
    PS C:\scripts> $error[0]|gm
    
    
       TypeName: System.Management.Automation.ErrorRecord
    
    Name                  MemberType     Definition
    ----                  ----------     ----------
    Equals                Method         bool Equals(System.Object obj)
    GetHashCode           Method         int GetHashCode()
    GetObjectData         Method         void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context), ...
    GetType               Method         type GetType()
    ToString              Method         string ToString()
    CategoryInfo          Property       System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;}
    ErrorDetails          Property       System.Management.Automation.ErrorDetails ErrorDetails {get;set;}
    Exception             Property       System.Exception Exception {get;}
    FullyQualifiedErrorId Property       string FullyQualifiedErrorId {get;}
    InvocationInfo        Property       System.Management.Automation.InvocationInfo InvocationInfo {get;}
    PipelineIterationInfo Property       System.Collections.ObjectModel.ReadOnlyCollection[int] PipelineIterationInfo {get;}
    ScriptStackTrace      Property       string ScriptStackTrace {get;}
    TargetObject          Property       System.Object TargetObject {get;}
    PSMessageDetails      ScriptProperty System.Object PSMessageDetails {get=& { Set-StrictMode -Version 1; $this.Exception.InnerException.PSMessageDetails };}
    
    
    PS C:\scripts>
    

    Note that the objects are different types.


    \_(ツ)_/

    Tuesday, June 12, 2018 3:39 PM
  • You can unbury the info like this:

    $erruser[0].ErrorRecord.InvocationInfo


    \_(ツ)_/

    • Marked as answer by PeteMitch99 Tuesday, June 12, 2018 4:53 PM
    Tuesday, June 12, 2018 3:41 PM
  • This works as expected for me:

    PS D:\scripts> Test-Connection "test1" -Count 1 -ea SilentlyContinue -ErrorVariable errtc
    PS D:\scripts> $errtc[0].InvocationInfo
    
    
    MyCommand             : Test-Connection
    BoundParameters       : {}
    UnboundArguments      : {}
    ScriptLineNumber      : 1
    OffsetInLine          : 1
    HistoryId             : 198
    ScriptName            :
    Line                  : Test-Connection "test1" -Count 1 -ea SilentlyContinue -ErrorVariable errtc
    PositionMessage       : At line:1 char:1
                            + Test-Connection "test1" -Count 1 -ea SilentlyContinue -ErrorVariable  ...
                            + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    PSScriptRoot          :
    PSCommandPath         :
    InvocationName        : Test-Connection
    PipelineLength        : 0
    PipelinePosition      : 0
    ExpectingInput        : False
    CommandOrigin         : Internal
    DisplayScriptPosition :
    
    
    


    \_(ツ)_/

    Tuesday, June 12, 2018 3:45 PM
  • I suspect that you can check the type and choose the unbundling method.

    if($errtc[0].GetType().Name -eq 'ErrorRecord'){
          $errtc[0].InvocationInfo 
    }else{
         $errtc[0].ErrorRecord.InvocationInfo
    }

    Some CmdLets from third parties may throw native exceptions which may not be handled in this way.


    \_(ツ)_/




    • Edited by jrv Tuesday, June 12, 2018 3:50 PM
    • Marked as answer by PeteMitch99 Tuesday, June 12, 2018 4:53 PM
    Tuesday, June 12, 2018 3:47 PM
  • Ok. I get that thanks. But if we do the same with my second example below the -ev is still a System.Management.Automation.ErrorRecord?

    Why does the type change for one Powershell Cmd and not for another? Why does it have to change at all? How is it possible the test-connection command work if those fields are not added until later in $error? Why is it inconsistent?
    PS C:\Users\118786\Desktop> $errtc|gm
    
       TypeName: System.Management.Automation.ErrorRecord
    
    Name                  MemberType     Definition                                                                                                    
    ----                  ----------     ----------                                                                                                    
    Equals                Method         bool Equals(System.Object obj)                                                                                
    GetHashCode           Method         int GetHashCode()                                                                                             
    GetObjectData         Method         void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.Stream...
    GetType               Method         type GetType()                                                                                                
    ToString              Method         string ToString()                                                                                             
    CategoryInfo          Property       System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;}                                            
    ErrorDetails          Property       System.Management.Automation.ErrorDetails ErrorDetails {get;set;}                                             
    Exception             Property       System.Exception Exception {get;}                                                                             
    FullyQualifiedErrorId Property       string FullyQualifiedErrorId {get;}                                                                           
    InvocationInfo        Property       System.Management.Automation.InvocationInfo InvocationInfo {get;}                                             
    PipelineIterationInfo Property       System.Collections.ObjectModel.ReadOnlyCollection[int] PipelineIterationInfo {get;}                           
    ScriptStackTrace      Property       string ScriptStackTrace {get;}                                                                                
    TargetObject          Property       System.Object TargetObject {get;}                                                                             
    PSMessageDetails      ScriptProperty System.Object PSMessageDetails {get=& { Set-StrictMode -Version 1; $this.Exception.InnerException.PSMessage...
    
    Do you think if i declared the -ev as that type first it would work?


    Tuesday, June 12, 2018 3:49 PM
  • You can also test objects types like this:

    $errtc[0] -is [System.Management.Automation.ErrorRecord]


    \_(ツ)_/

    Tuesday, June 12, 2018 3:54 PM
  • Ok. I get that thanks. But if we do the same with my second example below the -ev is still a System.Management.Automation.ErrorRecord?

    Why does the type change for one Powershell Cmd and not for another? Why does it have to change at all? How is it possible the test-connection command work if those fields are not added until later in $error? Why is it inconsistent?
    PS C:\Users\118786\Desktop> $errtc|gm
    
       TypeName: System.Management.Automation.ErrorRecord
    
    Name                  MemberType     Definition                                                                                                    
    ----                  ----------     ----------                                                                                                    
    Equals                Method         bool Equals(System.Object obj)                                                                                
    GetHashCode           Method         int GetHashCode()                                                                                             
    GetObjectData         Method         void GetObjectData(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.Stream...
    GetType               Method         type GetType()                                                                                                
    ToString              Method         string ToString()                                                                                             
    CategoryInfo          Property       System.Management.Automation.ErrorCategoryInfo CategoryInfo {get;}                                            
    ErrorDetails          Property       System.Management.Automation.ErrorDetails ErrorDetails {get;set;}                                             
    Exception             Property       System.Exception Exception {get;}                                                                             
    FullyQualifiedErrorId Property       string FullyQualifiedErrorId {get;}                                                                           
    InvocationInfo        Property       System.Management.Automation.InvocationInfo InvocationInfo {get;}                                             
    PipelineIterationInfo Property       System.Collections.ObjectModel.ReadOnlyCollection[int] PipelineIterationInfo {get;}                           
    ScriptStackTrace      Property       string ScriptStackTrace {get;}                                                                                
    TargetObject          Property       System.Object TargetObject {get;}                                                                             
    PSMessageDetails      ScriptProperty System.Object PSMessageDetails {get=& { Set-StrictMode -Version 1; $this.Exception.InnerException.PSMessage...
    Do you think if i declared the -ev as that type first it would work?


    Remember this:

    "Daddy, why is the sky blue?"
    "Because Sally, it is to match your eyes."


    \_(ツ)_/

    Tuesday, June 12, 2018 3:57 PM
  • $erruser[0].ErrorRecord.InvocationInfo

    Thanks jrv this is very helpful suggestion - i couldn't find it. At least the info is still there and therefore yes I could do a 'if statement' based on the type as you suggest. So i will try this.

    The error structure of a

    TypeName: System.Management.Automation.ErrorRecord

    and

    TypeName: System.Management.Automation.ParseException (or other exception)

    are certainly different and I have seen occasions where there are both types in the error variable.

    However it would be useful to know if you think i could force\declare my -ev as a certain type to minimize this impact? I really would like to get the type behavior of ev consistent on my machine if possible. if you have any suggestions re this let me know.

    Thanks

    Pete



    • Edited by PeteMitch99 Thursday, June 28, 2018 12:29 PM
    Tuesday, June 12, 2018 4:04 PM
  • I have updated my script with this bit of code to make the locations consistent.

    #If the individual error gettype equals 'ErrorRecord' then set correct path for Errorpath properties, if not set path for CmdletInvocationException if($OrgErr.GetType().Name -eq 'ErrorRecord'){ #for- TypeName: System.Management.Automation.ErrorRecord $ErrInvPath = $OrgErr.InvocationInfo $ErrCatPath = $OrgErr.categoryinfo $ErrExcPath = $OrgErr.exception }else{ #TypeName: System.Management.Automation.CmdletInvocationException (or any other exception) $ErrInvPath = $OrgErr.ErrorRecord.InvocationInfo $ErrCatPath = $OrgErr.ErrorRecord.categoryinfo $ErrExcPath = $OrgErr}

    #Calls $ErrInvPath.line #psline code $ErrInvPath.pscommandpath #path $ErrInvPath.ScriptLineNumber #linenum $ErrInvPath.OffsetInLine #offset $ErrCatPath.Activity #pstool $ErrCatPath.Reason #reason $ErrExcPath.message #exception message








    • Edited by PeteMitch99 Thursday, June 28, 2018 12:38 PM
    • Marked as answer by PeteMitch99 Thursday, June 28, 2018 12:39 PM
    Thursday, June 28, 2018 12:29 PM
  • I didn't know about format-list -force.  I don't like that tab completion doesn't show all the $error properties.


    • Edited by JS2010 Thursday, June 28, 2018 2:44 PM
    Thursday, June 28, 2018 2:43 PM