Visitar URL original
`PSObject.ToString` mishandles `TypeCode`, throws `InvalidCastException` for otherwise good objects · Issue #28034 · PowerShell/PowerShell · GitHub
Skip to content

PSObject.ToString mishandles TypeCode, throws InvalidCastException for otherwise good objects #28034

Description

@GeeLaw

Prerequisites

Steps to reproduce

$code = '
class Example : System.IConvertible
{
[string] ToString () { Return "object.ToString" }
[string] ToString ([System.IFormatProvider]$fmt) { Return "IConvertible.ToString" }
[TypeCode] GetTypeCode() { Return [TypeCode]::String }
[object] ToType ([type]$type, [System.IFormatProvider]$fmt) { Throw "IConvertible.ToType" }
';
$code += (@('Boolean', 'Char', 'SByte', 'Byte',
  'Int16', 'UInt16', 'Int32', 'UInt32', 'Int64', 'UInt64',
  'Single', 'Double', 'Decimal', 'DateTime' # , 'String'
) | ForEach-Object {
  "[$_] To$_ ([System.IFormatProvider]`$fmt) { Throw 'IConvertible.To$_' }"
}) -join "`n";
$code += '
}

[Example]::new().ToString(); # object.ToString
"" + [Example]::new();       # PSObject.ToString -> InvalidCastException
';

Write-Verbose $code;
Invoke-Expression $code;

Expected behavior

Outputs object.ToString twice.

Actual behavior

object.ToString
OperationStopped:
Line |
  25 |  "" + [Example]::new();       # PSObject.ToString -> InvalidCastExcept …
     |  ~~~~~~~~~~~~~~~~~~~~~
     | Unable to cast object of type 'Example' to type 'System.String'.

Error details

The technical analysis is in the first comment by me, rendering the error details irrelevant.

Environment data

Name                           Value
----                           -----
PSVersion                      7.6.6
PSEdition                      Core
GitCommitId                    7.6.6
OS                             Microsoft Windows 10.0.26200
Platform                       Win32NT
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0…}
PSRemotingProtocolVersion      2.4
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

Visuals

No response

Activity

  1. GeeLaw commented on Sep 19, 2026

    @GeeLaw
    Author

    In PSObject.ToString, PowerShell tries to fast-track “common simple” types. It detects whether value is a common simple type using Convert.GetTypeCode(value). If it returns TypeCode.String, the code then tries to cast value to string (and to IFormattable or concrete types for other various type codes).

    This is wrong. There are primarly 3 methods to get TypeCode, with semantics explained below.

    • TypeCode IConvertible.GetTypeCode() is an interface method hence polymorphic on each object. If an object implements IConvertible, its GetTypeCode is supposed to return the type code describing the data it wraps (if it wraps a single value of a common simple type), or TypeCode.Object if not applicable. Notably, if T implements IConvertible, there’s no reason to believe all instances of T return the same TypeCode from this method.
    • TypeCode Type.GetTypeCode(Type type) is a static method and obtains the type code of type statically, which is determined as follows: if it’s the actual “common simple type” corresponding to a TypeCode (e.g., int/string for Int32/String), then return it; if it’s an enum, then return the underlying type’s type code; otherwise, return TypeCode.Object. This method doesn’t care if Type implements IConvertible.
    • TypeCode Convert.GetTypeCode(object obj) is a static method and serves as a universal way to determine the type code polymorphically, falling back to TypeCode.Object if obj doesn’t implement IConvertible.

    It is perfectly valid for a type that is not string to implement IConvertible and return TypeCode.String, e.g., a UTF-8/32 string type. It’s also perfectly valid if a type returns TypeCode.UInt32 without being IFormattable, or returns TypeCode.Double without being double.


    The correct type code to check is Type.GetTypeCode(obj.GetType()) (PowerShell has an internal extension method on Type so that you can say obj.GetType().GetTypeCode()). Note that Windows PowerShell 5.1 doesn’t have this bug, because it does the right thing.

    This bug was introduced 7 years ago in PowerShell/PowerShell#9893, affecting PowerShell 7.0.0-preview.2 to 7.6.6 and 7.7.0-preview.1 to 7.7.0-preview.4.

  2. GeeLaw commented on Sep 19, 2026

    @GeeLaw
    Author

    I am happy to offer a PR for this issue. If you’re happy to let me take a stab, please assign this issue to me.

    Cross-referencing: dotnet/runtime#134285

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

    Needs-TriageThe issue is new and needs to be triaged by a work group.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions