Martin Ba
Martin Ba

Reputation: 38941

Pwsh $Null parameter error is reported less verbose than other errors?

Why is Powershell 7 (pwsh.exe) NOT outputting the full error when we have a $null passed to a non-null parameter? And how can I configure my Powershell 7 on my Windows 10 box to fully report it?

Given this script:

param(
  [Parameter()]
  $Adventurer = "Bilbo"
)

$ErrorActionPreference = 'Stop'
#Requires -Version 7

function Get-SpyResult($FileName) {
  $profile = Get-Item $FileName
  # ...
}

$x = Get-SpyResult $Adventurer
# ...
Write-Host "If this worked, we would get here."

We can observe the following:

PS C:\temp> $PSVersionTable.PSVersion.ToString()
7.4.1
PS C:\temp> .\here_be_dragons.ps1 "Someone"
Get-Item: C:\temp\here_be_dragons.ps1:10
Line |
  10 |    $profile = Get-Item $FileName
     |               ~~~~~~~~~~~~~~~~~~
     | Cannot find path 'C:\temp\Someone' because it does not exist.
PS C:\temp>
PS C:\temp> .\here_be_dragons.ps1 $Null
here_be_dragons.ps1: Cannot bind argument to parameter 'Path' because it is null.
PS C:\temp> # Why ??
PS C:\temp>
PS C:\temp> $Error[0]
Get-Item: C:\temp\here_be_dragons.ps1:10
Line |
  10 |    $profile = Get-Item $FileName
     |                        ~~~~~~~~~
     | Cannot bind argument to parameter 'Path' because it is null.

PS C:\temp>

As you can see, Cannot find path is reported with a stack trace.

But Cannot bind argument to parameter is not properly reported, you have to resolve the error object manually.

Now, by inspecting the error objects, we can get a clue:

When it works, we have this:

_> $Error[0].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    ActionPreferenceStopException            System.Management.Automation.RuntimeException

For the $null mess we have:

_> $Error[0].GetType()

IsPublic IsSerial Name                                     BaseType
-------- -------- ----                                     --------
True     False    ErrorRecord                              System.Object

The actual error objects seem to be generated differently. But that still doesn't explain what this is all about and how I can get my scripts to always report a stack trace.

Upvotes: 1

Views: 52

Answers (2)

ninMonkey
ninMonkey

Reputation: 7501

Part of the issue might be you're executing a script from inside a script, and not dot sourcing it.

output of enforcing error

You don't have to deal with that extra layer if your script defines functions.

Sometimes it's better to error on Parameter Binding.

Other times it's better to validate after you bind. here I use Get-Item -ErrorAction Stop to ensure the file exists.

function Get-SpyOnParamBind { 
  [cmdletBinding()]
  param(
    [ValidateNotNullOrEmpty()]
    [Parameter()] $Filename = 'default.json'
  )

  "Config: ${Filename}"
}

function Get-SpyOnFileMissing {
  [CmdletBinding()]
  param(
    [Parameter()] $Filename = 'default.json'
  )
  $config = Get-Item -ea 'stop' $Filename
  "Config: ${Filename}"
}

output of get-error

What could be happening is there's more than one error record being created. Check out $error.count to confirm it.

$error | Join-String -sep "`n`n#####`n`n" -P { $_.Exception }
## or 
$error | fl * -force

errors on get item

Upvotes: 0

Martin Ba
Martin Ba

Reputation: 38941

The problem is that - at least for 7.4.1 - the error display is basically broken/buggy in that it is completely inconsistent. (Both for the COnciseView as well as the NormalView, see below.)

If you run the case above

PS C:\temp> .\here_be_dragons.ps1 $Null
here_be_dragons.ps1: Cannot bind argument to parameter 'Path' because it is null.
PS C:\temp> # Why ??

with ErrorActionPreference = Continue, then it will even print the stack trace! As opposed to ErrorActionPreference=Stop.


A current solution / workaround lies in the $ErrorView variable and in the Get-Error commandlet of pwsh 7:

  • You can use Get-Error to get a (waaay too verbose!) complete view on the last error
  • You can set $ErrorView = 'DetailedView' to get the same dump of info as with Get-Error
  • Do not use $Error[0]

From what you get back from Get-Error, you are looking for the InvocationInfo and the ScriptStackTrace to identify the location in your script where the error occured.

Upvotes: 0

Related Questions