Sitelet https://github.com/PowerShell/PowerShell/issues/27842
Skip to content

AvoidReservedCharInCmdlet intermittently throws NullReferenceException from CommandInfo.ResolveParameter #27842

Description

@dgalbraith

Prerequisites

Steps to reproduce

Invoke-ScriptAnalyzer intermittently fails with a NullReferenceException when analysing a file that both defines a function and calls Export-ModuleMember. The exception surfaces in AvoidReservedCharInCmdlet, by way of Helper.GetExportedFunction calling CommandInfo.ResolveParameter, and reproduces on the current release in 14/19 of 100 fresh sessions against a four-line file. Under -ErrorAction Stop it terminates the call; under the default preference it can discard the file's diagnostics and still exit zero.

Related: PowerShell/PSScriptAnalyzer#1538 (closed without a reproducer; identical stack), PowerShell/PSScriptAnalyzer#1708 (open; same CommandInfo root, different rule), PowerShell/PSScriptAnalyzer#1351 (open; transient RULE_ERROR, rule unidentified).

Steps to reproduce

  • Requires the PSScriptAnalyzer module ('Install-Module PSScriptAnalyzer').
  • Analysing a file that both defines a function and calls Export-ModuleMember intermittently fails with a NullReferenceException thrown from System.Management.Automation.CommandInfo.ResolveParameter.
  • Neither element on its own reproduces it.

This writes repro.ps1 into the current directory and analyses it there.

# Four lines are enough; nothing else in the file matters.
@'
function Get-Thing {
    return 1
}
Export-ModuleMember -Function Get-Thing
'@ | Set-Content ./repro.ps1

# One analysis per fresh session: the fault surfaces far more readily on the
# first invocation in a process than on later ones.
$threw = 0
1..100 | ForEach-Object {
    $out = pwsh -NoProfile -Command {
        Import-Module PSScriptAnalyzer -ErrorAction Stop
        $ErrorActionPreference = 'Stop'
        try { $null = @(Invoke-ScriptAnalyzer -Path ./repro.ps1); 'OK' } catch { 'THREW' }
    }
    if ($out -match 'THREW') { $threw++ }
}
"threw $threw / 100"

Observations

One hundred fresh sessions per row, on 1.25.0 except the last:

Input or option Threw
The four-line file above 14 / 100; 19 / 100
Function only, no Export-ModuleMember 0 / 100
Export-ModuleMember only, no function 0 / 100
-ExcludeRule PSReservedCmdletChar 0 / 100
-ExcludeRule PSProvideCommentHelp 0 / 100
-Severity Warning 0 / 100
The same file on 1.24.0 11 / 100; 13 / 100
  • The extension is irrelevant: .ps1 and .psm1 reproduce alike, in any directory. Get-Thing contains no reserved character, so the rule reports nothing on this file even on a successful run.
  • Under $ErrorActionPreference = 'Stop' the exception terminates the call, rather than being reported as a non-terminating error.
  • Under the default preference the run continues and the diagnostic can be lost silently.
  • An error-free run returns one finding, PSProvideCommentHelp.
  • The rate is not stable between measurement runs on identical input, which seems consistent with a race condition.
  • The stack names AvoidReservedCharInCmdlet, yet excluding PSProvideCommentHelp also stops it. Both rules reach Helper.GetExportedFunction, so a second concurrent caller appears to be required rather than the throwing rule alone.
  • -Severity Warning masks it because that filter suppresses rule selection rather than filtering diagnostics, so PSProvideCommentHelp never runs. See Invoke-ScriptAnalyzer -Severity filters rules rather than diagnostics PSScriptAnalyzer#2156.

Expected behavior

Analysis completes and returns the file's diagnostics. No error is written.

Actual behavior

On a failing run, `Invoke-ScriptAnalyzer` writes:


Invoke-ScriptAnalyzer: Object reference not set to an instance of an object.


- Under `$ErrorActionPreference = 'Stop'` the exception terminates the call.
- Under the default preference the run continues and the diagnostic can be lost silently: an error-free run returns one finding, `PSProvideCommentHelp`.
- The rate is not stable across measurement runs on identical input, consistent with a
race condition.
- Repeated 100-session runs gave 14 and 19 on PSScriptAnalyzer 1.25.0, with 11 and 13 on
1.24.0.
- Excluding either `PSReservedCmdletChar` or `PSProvideCommentHelp` gives 0/100;
both rules reach `Helper.GetExportedFunction`, so a second concurrent caller appears to
be required rather than the throwing rule alone.

Error details

**Error details** — `Get-Error`, verbatim, paths sanitised:


Exception             :
Type       : System.NullReferenceException
TargetSite :
Name          : ResolveParameter
DeclaringType : [System.Management.Automation.CommandInfo]
MemberType    : Method
Module        : System.Management.Automation.dll
Message    : Object reference not set to an instance of an object.
Source     : System.Management.Automation
HResult    : -2147467261
StackTrace :
at System.Management.Automation.CommandInfo.ResolveParameter(String name)
at Microsoft.Windows.PowerShell.ScriptAnalyzer.Helper.GetExportedFunction(Ast ast)
at Microsoft.Windows.PowerShell.ScriptAnalyzer.BuiltinRules.AvoidReservedCharInCmdlet.AnalyzeScript(Ast ast, String fileName)+MoveNext()
at System.Collections.Generic.List1..ctor(IEnumerable1 collection)
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at Microsoft.Windows.PowerShell.ScriptAnalyzer.ScriptAnalyzer.<>c__DisplayClass84_1.<AnalyzeSyntaxTree>b__2()
TargetObject          : /path/to/repro.ps1
CategoryInfo          : InvalidOperation: (/path/to/repro.ps1:String) [Invoke-ScriptAnalyzer], NullReferenceException
FullyQualifiedErrorId : RULE_ERROR,Microsoft.Windows.PowerShell.ScriptAnalyzer.Commands.InvokeScriptAnalyzerCommand
InvocationInfo        :
MyCommand        : Invoke-ScriptAnalyzer
ScriptLineNumber : 3
OffsetInLine     : 15
InvocationName   : Invoke-ScriptAnalyzer
CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 3

Environment data

> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      7.6.4
PSEdition                      Core
GitCommitId                    7.6.4
OS                             Debian GNU/Linux 13 (trixie)
Platform                       Unix

- `PSScriptAnalyzer` 1.24.0 installed
- 1.25.0 (current PSGallery release) tested via `Save-Module` and an explicit manifest import.
- Both versions affected.
- Not tested on Windows or macOS.

Visuals

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Needs-TriageThe issue is new and needs to be triaged by a work group.WG-Enginecore PowerShell engine, interpreter, and runtime

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions