Skip to content

Binary cmdlet binding fails to unwrap a scalar PSObject for StringCollection parameters #27969

Closed

Description

Prerequisites

Steps to reproduce

$source = @'
using System.Collections.Specialized;
using System.Management.Automation;

[Cmdlet("Test", "StringCollectionBinding")]
public sealed class TestStringCollectionBindingCommand : PSCmdlet
{
    [Parameter(Mandatory = true)]
    public StringCollection Value { get; set; }

    protected override void ProcessRecord()
    {
        WriteObject(Value[0]);
    }
}
'@

$type = Add-Type -TypeDefinition $source -PassThru
Import-Module -Assembly $type.Assembly

# Select-Object emits one scalar PSObject wrapping a string.
$value = 'test-value' | Select-Object -Unique

$value.GetType().FullName
$value -is [System.Management.Automation.PSObject]

Test-StringCollectionBinding -Value $value

# The control case succeeds:
Test-StringCollectionBinding -Value ([string[]]$value)

# Multiple pipeline results also succeed:
$values = 'first', 'second' | Select-Object -Unique
Test-StringCollectionBinding -Value $values

Expected behavior

System.String
True
test-value

Actual behavior

System.String
True

Test-StringCollectionBinding:
Cannot convert 'expected' to the type
'System.Collections.Specialized.StringCollection' required by parameter
'Value'. Unable to cast object of type
'System.Management.Automation.PSObject' to type 'System.String'.

Error details

Exception             : 
    Type              : System.Management.Automation.ParameterBindingException
    Message           : Cannot convert 'test-value' to the type 'System.Collections.Specialized.StringCollection' required by parameter 'Value'. Unable to cast object of type 'System.Management.Automation.PSObject' to type 'System.String'.
    ParameterName     : Value
    ParameterType     : [System.Collections.Specialized.StringCollection]
    TypeSpecified     : [System.Management.Automation.PSObject]
    ErrorId           : CannotConvertArgument
    Line              : 22
    Offset            : 37
    CommandInvocation : 
        MyCommand        : Test-StringCollectionBinding
        ScriptLineNumber : 22
        OffsetInLine     : 1
        HistoryId        : 1
        Line             : Test-StringCollectionBinding -Value $value 2>$null

        Statement        : Test-StringCollectionBinding -Value $value 2>$null
        PositionMessage  : At line:22 char:1
                           + Test-StringCollectionBinding -Value $value 2>$null
                           + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        InvocationName   : Test-StringCollectionBinding
        PipelineLength   : 1
        PipelinePosition : 1
    ErrorRecord       : 
        Exception             : 
            Type    : System.Management.Automation.ParentContainsErrorRecordException
            Message : Cannot convert 'test-value' to the type 'System.Collections.Specialized.StringCollection' required by parameter 'Value'. Unable to cast object of type 'System.Management.Automation.PSObject' to type 'System.String'.
            HResult : -2146233087
        CategoryInfo          : InvalidArgument: (:) [Test-StringCollectionBinding], ParentContainsErrorRecordException
        FullyQualifiedErrorId : CannotConvertArgument,TestStringCollectionBindingCommand
        InvocationInfo        : 
            MyCommand        : Test-StringCollectionBinding
            ScriptLineNumber : 22
            OffsetInLine     : 37
            HistoryId        : 1
            Line             : Test-StringCollectionBinding -Value $value 2>$null

            Statement        : $value
            PositionMessage  : At line:22 char:37
                               + Test-StringCollectionBinding -Value $value 2>$null
                               +                                     ~~~~~~
            CommandOrigin    : Internal
        ScriptStackTrace      : at <ScriptBlock>, <No file>: line 22
    TargetSite        : 
        Name          : EncodeCollection
        DeclaringType : [System.Management.Automation.ParameterBinderBase]
        MemberType    : Method
        Module        : System.Management.Automation.dll
    Data              : 
        System.Management.Automation.Interpreter.InterpretedFrameInfo, System.Management.Automation, Version=7.6.0.500, Culture=neutral, PublicKeyToken=31bf3856ad364e35 : <ScriptBlock>

    InnerException    : 
        Type       : System.InvalidCastException
        TargetSite : 
            Name          : ChkCastAny_NoCacheLookup
            DeclaringType : [System.Runtime.CompilerServices.CastHelpers]
            MemberType    : Method
            Module        : System.Private.CoreLib.dll
        Message    : Unable to cast object of type 'System.Management.Automation.PSObject' to type 'System.String'.
        Source     : System.Private.CoreLib
        HResult    : -2147467262
        StackTrace : 
   at System.Collections.Specialized.StringCollection.System.Collections.IList.Add(Object value)
   at System.Management.Automation.ParameterBinderBase.EncodeCollection(CommandParameterInternal argument, String parameterName, ParameterCollectionTypeInformation collectionTypeInformation, Type toType, Object currentValue, Boolean 
coerceElementTypeIfNeeded, Boolean& coercionRequired)
    Source            : System.Management.Automation
    HResult           : -2146233087
    StackTrace        : 
   at System.Management.Automation.ParameterBinderBase.EncodeCollection(CommandParameterInternal argument, String parameterName, ParameterCollectionTypeInformation collectionTypeInformation, Type toType, Object currentValue, Boolean 
coerceElementTypeIfNeeded, Boolean& coercionRequired)
   at System.Management.Automation.ParameterBinderBase.CoerceTypeAsNeeded(CommandParameterInternal argument, String parameterName, Type toType, ParameterCollectionTypeInformation collectionTypeInfo, Object currentValue)
   at System.Management.Automation.ParameterBinderBase.BindParameter(CommandParameterInternal parameter, CompiledCommandParameter parameterMetadata, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter, ParameterBindingFlags flags)
   at System.Management.Automation.CmdletParameterBinderController.BindNamedParameter(UInt32 parameterSets, CommandParameterInternal argument, MergedCompiledCommandParameter parameter)
   at System.Management.Automation.ParameterBinderController.BindNamedParameters(UInt32 parameterSets, Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParametersNoValidation(Collection`1 arguments)
   at System.Management.Automation.CmdletParameterBinderController.BindCommandLineParameters(Collection`1 arguments)
   at System.Management.Automation.CommandProcessor.BindCommandLineParameters()
   at System.Management.Automation.CommandProcessor.Prepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.CommandProcessorBase.DoPrepare(IDictionary psDefaultParameterValues)
   at System.Management.Automation.Internal.PipelineProcessor.Start(Boolean incomingStream)
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
--- End of stack trace from previous location ---
   at System.Management.Automation.Internal.PipelineProcessor.SynchronousExecuteEnumerate(Object input)
   at System.Management.Automation.PipelineOps.InvokePipeline(Object input, Boolean ignoreInput, CommandParameterInternal[][] pipeElements, CommandBaseAst[] pipeElementAsts, CommandRedirection[][] commandRedirections, FunctionContext 
funcContext)
   at System.Management.Automation.Interpreter.ActionCallInstruction`6.Run(InterpretedFrame frame)
   at System.Management.Automation.Interpreter.EnterTryCatchFinallyInstruction.Run(InterpretedFrame frame)
CategoryInfo          : InvalidArgument: (:) [Test-StringCollectionBinding], ParameterBindingException
FullyQualifiedErrorId : CannotConvertArgument,TestStringCollectionBindingCommand
InvocationInfo        : 
    MyCommand        : Test-StringCollectionBinding
    ScriptLineNumber : 22
    OffsetInLine     : 37
    HistoryId        : 1
    Line             : Test-StringCollectionBinding -Value $value 2>$null

    Statement        : $value
    PositionMessage  : At line:22 char:37
                       + Test-StringCollectionBinding -Value $value 2>$null
                       +                                     ~~~~~~
    CommandOrigin    : Internal
ScriptStackTrace      : at <ScriptBlock>, <No file>: line 22

Environment data

Name                           Value
----                           -----
PSVersion                      7.6.5
PSEdition                      Core
GitCommitId                    7.6.5
OS                             Microsoft Windows 10.0.26200
Platform                       Win32NT
PSRemotingProtocolVersion      2.4
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

Activity

  1. daksh-goyal commented on Sep 3, 2026

    @daksh-goyal
    Author

    Source analysis

    ParameterBinderBase.EncodeCollection() handles list and scalar inputs inconsistently.

    The list-input branch correctly unwraps each element:

    object currentValueElement = PSObject.Base(valueElement);

    The scalar-input branch does not unwrap currentValue before calling IList.Add() :

    Suggested fix

    Make the scalar path mirror the list path. When coercion is required, preserve the original wrapped value as the input and assign the converted result to currentValueElement.

  2. SeeminglyScience commented on Oct 5, 2026

    @SeeminglyScience
    Contributor

    The Engine WG discussed this. We presume this is due to StringCollection not implementing IList and therefore the parameter binder assumes it accepts any type. While we recommend moving away from StringCollection if possible, we do agree this should be fixed. Since we cannot determine statically if the list accepts generic objects (when it does not implement IList<>) we recommend special casing StringCollection, that way ETS members are preserved otherwise.

  3. added
    Up-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributors
    WG-ReviewedA Working Group has reviewed this and made a recommendation
    WG-TriagedThe issue has been triaged by the designated WG.
    and removed
    WG-NeedsReviewNeeds a review by the labeled Working Group
    Needs-TriageThe issue is new and needs to be triaged by a work group.
    WG-TriagedThe issue has been triaged by the designated WG.
    on Oct 5, 2026
  4. added and removed
    Up-for-GrabsUp-for-grabs issues are not high priorities, and may be opportunities for external contributors
    on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-BugIssue has been identified as a bug in the productResolution-FixedThe issue is fixed.WG-Enginecore PowerShell engine, interpreter, and runtimeWG-ReviewedA Working Group has reviewed this and made a recommendation

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions