Skip to content

NativeAOT MethodInvoker.Invoke(object, Span<object>) throws InvalidOperationException instead of TargetParameterCountException for a wrong argument count on an open generic method #136102

Open

Description

@caraioniurie47

Description

MethodInvoker.Invoke(object?, Span<object?>) with the wrong number of arguments for a method of an open generic type or a generic method definition throws TargetParameterCountException on CoreCLR and InvalidOperationException on NativeAOT. The two-argument Invoke(object?, object?, object?) overload throws TargetParameterCountException on both runtimes.

This came up in #135566, whose "Not changed" section lists it.

Reproduction Steps

using System;
using System.Reflection;

MethodInfo openType = typeof(C<>).GetMethod(nameof(C<int>.OneArg))!;
MethodInfo definition = typeof(C).GetMethod(nameof(C.GenericOneArg))!;

foreach (MethodInfo method in new[] { openType, definition })
{
    MethodInvoker invoker = MethodInvoker.Create(method);
    string name = $"{method.DeclaringType!.Name}.{method.Name}";
    Run($"{name}, Span with 2 arguments", () => invoker.Invoke(null, new Span<object?>(new object?[] { 1, 2 })));
    Run($"{name}, Span with 0 arguments", () => invoker.Invoke(null, new Span<object?>(new object?[0])));
    Run($"{name}, 2 fixed arguments", () => invoker.Invoke(null, 1, 2));
}

static void Run(string label, Action action)
{
    try
    {
        action();
        Console.WriteLine($"{label}: no exception");
    }
    catch (Exception e)
    {
        Console.WriteLine($"{label}: {e.GetType().Name}");
    }
}

static class C
{
    public static T GenericOneArg<T>(T value) => value;
}

static class C<T>
{
    public static T OneArg(T value) => value;
}

Expected behavior

The same on both runtimes. Proposed: CoreCLR's:

C`1.OneArg, Span with 2 arguments: TargetParameterCountException
C`1.OneArg, Span with 0 arguments: TargetParameterCountException
C`1.OneArg, 2 fixed arguments: TargetParameterCountException
C.GenericOneArg, Span with 2 arguments: TargetParameterCountException
C.GenericOneArg, Span with 0 arguments: TargetParameterCountException
C.GenericOneArg, 2 fixed arguments: TargetParameterCountException

Other option considered: InvalidOperationException from the span overload on both runtimes: the two-argument overload already reports the wrong count with TargetParameterCountException on both.

Actual behavior

CoreCLR prints the output above. NativeAOT:

C`1.OneArg, Span with 2 arguments: InvalidOperationException
C`1.OneArg, Span with 0 arguments: InvalidOperationException
C`1.OneArg, 2 fixed arguments: TargetParameterCountException
C.GenericOneArg, Span with 2 arguments: InvalidOperationException
C.GenericOneArg, Span with 0 arguments: InvalidOperationException
C.GenericOneArg, 2 fixed arguments: TargetParameterCountException

Regression?

No. On .NET 9.0.21 the repro prints the same outputs as above. On 8.0.32 the open generic type gives the same outputs; for the generic method definition, NativeAOT already throws NotSupportedException from MethodInvoker.Create.

Known Workarounds

None needed: the call fails on both runtimes; only the exception type differs.

Configuration

.NET 9.0.21 and 10.0.13 (SDK 10.0.402) and 11.0.0-rc.1.26420.103 (SDK 11.0.100-rc.1.26420.103), Windows 11 x64; also 8.0.32 (SDK 10.0.402); NativeAOT published with dotnet publish -c Release -r win-x64 -p:PublishAot=true.

Other information

CoreCLR's span overload checks the count first:

MethodInvoker.cs:242-248

        public object? Invoke(object? obj, Span<object?> arguments)
        {
            int argLen = arguments.Length;
            if (argLen != _argCount)
            {
                MethodBaseInvoker.ThrowTargetParameterCountException();
            }

NativeAOT's fixed-arity overloads check it too, but its span overload passes the arguments to the method's invoker, which for an open generic method is OpenMethodInvoker and throws InvalidOperationException without looking at them:

MethodInvoker.cs:131-136

        public object? Invoke(object? obj, Span<object?> arguments)
        {
            object? result = _methodBaseInvoker.Invoke(obj, arguments);
            DebugAnnotations.PreviousCallContainsDebuggerStepInCode();
            return result;
        }

I'm working on a fix and will open a PR for it. Could this be assigned to me?

Note

AI-generated, written at my direction and reviewed by me before posting. The repro above ran as dotnet exec (CoreCLR) and as a NativeAOT publish, with the SDKs and command under Configuration, on each release listed there.

Activity

  1. dotnet-policy-service commented on Oct 19, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @agocke, @dotnet/ilc-contrib
    See info in area-owners.md if you want to be subscribed.

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

    Type

    No type

    Projects

    • Status
      No status

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions