Skip to content

InvocationInfo reports incorrect position of exception generated by a key or property #15742

@scps-github

Description

@scps-github

Prerequisites

Steps to reproduce

This is my first bug report so just want to note:

  1. I love Powershell and have been using it productively for many years. I
    deeply appreciate the time and effort of everyone at MSFT and in the
    community.
  2. I apogise if this is a duplicate - did my best to search for an existing
    issue.
  3. My assessment of this bug may be misinformed or simply wrong.
  4. My knowledge of C# and Powershell's inner workings are lacking, but I am
    willing to put time into providing further information and/or prodding at
    source if anyone wishes to point me in the right direction. I had a scroll
    through some relevant-sounding files in
    PowerShell/src/Microsoft.PowerShell.ConsoleHost/host/msh/ but frankly
    have no idea what I'm even looking for.
  5. Sorry if I've screwed up anything else about the bug reporting process -
    let me know how to do it better next time.

This bug had me jumping at shadows in a somewhat more complicated structure
until I realised my simple error (a mis-assigned variable) was actually
positioned elsewhere from where it was being reported by the Powershell
interpreter.

I'm not especially familiar with Powershell exceptions, but from my limited
prodding it seems that $error[0].Exception contains the correct error message
(divide by zero), but the position of the error in the script as reported in
$error[0].InvocationInfo appears to be wrong in this circumstance.

The bug seems to arise when a key/property assignment generates an exception.
I first observed it in a PSCustomObject but you can see here it occurs in a
hashtable, so I expect it would appear in other contexts too. There seem to be
two slightly different forms:

The general case is that the exception generated by the key/property is
attributed to the parent object. This isn't necessarily wrong, but perhaps
isn't ideal. Compare for instance:

. {
    @{
        key1 = throw
        key2 = 'unrelated to error'
    }
}

Output:

Exception:
Line |
   3 |          key1 = throw
     |                 ~~~~~
     | ScriptHalted

this is a little more helpful than:

. {
    @{
        key1 = 1/0
        key2 = 'unrelated to error'
    }
}

Output

ParentContainsErrorRecordException:
Line |
   2 |      @{
     |      ~~
     | Attempted to divide by zero.

But the real problem is when there is a command substitution (i.e. $(..))
in another key/property that comes before the key/property that generates
the exception - in this case the exception is incorrectly attributed to the
completely unrelated command substitution.

below are a few more illustrative permutations:

# exception reported at outer object when exception comes first
. {
    @{                                  # exception reported line 2
        key1 = 1/0                      # exception actually on line 3
        key2 = $('unrelated to error')
    }
}

# exception reported at unrelated key when command substitution comes first
. {
    @{
        key1 = $('unrelated to error')  # exception reported line 3
        key2 = 1/0                      # exception actually on line 4
    }
}

# exception reported perfectly if itself is inside a command substitution
@{
    key1 = $('unrelated to error')
    key2 = $(1/0)                       # exception reported line 3
}

# throw does not trigger bug
@{
    key1 = $('unrelated to error')
    key2 = throw                        # exception reported line 3
}

# exception reported against unrelated key
@{
    key1 = $('unrelated to error')      # exception reported line 2
    key2 = 'also unrelated to error'
    key3 = 1/0                          # exception actually on line 4
}

# exception reported against unrelated key
@{
    key1 = 'unrelated to error'
    key2 = $('also unrelated to error') # exception reported line 3
    key3 = 1/0                          # exception actually on line 4
}

# exception reported against unrelated key
@{
    key1 = $('unrelated to error')
    key2 = $('also unrelated to error') # exception reported line 3
    key3 = 1/0                          # exception actually on line 4
}

Expected behavior

PS> @{
>    key1 = $('unrelated to error')
>    key2 = 1/0
>}
ParentContainsErrorRecordException:
Line |
   3 |      key2 = 1/0
     |             ~~~
     | Attempted to divide by zero.

Actual behavior

PS> @{
>    key1 = $('unrelated to error')
>    key2 = 1/0
>}
ParentContainsErrorRecordException:
Line |
   2 |      key1 = $('unrelated to error')
     |               ~~~~~~~~~~~~~~~~~~~~
     | Attempted to divide by zero.

Environment data

PS> $VersionTable
Name                           Value
----                           -----
PSVersion                      7.1.3
PSEdition                      Core
GitCommitId                    7.1.3
OS                             Microsoft Windows 10.0.19043
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-BugIssue has been identified as a bug in the productNeeds-TriageThe issue is new and needs to be triaged by a work group.WG-Languageparser, language semantics

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions