dotnet / dotnet/runtime

[mono][wasm] BadImageFormatException "Method has no body" when calling static abstract interface member through generic type parameter

Open
#124,443 2 comments 0 reactions 0 assignees View on GitHub
arch-wasm area-Codegen-Interpreter-mono
Dominant language
C#
Stars
18.3k
Forks
5.6k
PR merge metrics
PR metrics pending

Description

## Description

Calling a `static abstract` interface member through a generic type parameter throws `System.BadImageFormatException: Method has no body` in the Mono WASM interpreter. The same code works correctly on CoreCLR.

This appears to be a regression or gap related to #65384, which was fixed for the JIT path but may not cover the WASM interpreter when the static abstract member uses explicit interface implementation.

## Reproduction Steps

1. Create a Blazor WebAssembly (standalone) app targeting `net10.0`
2. Add the following types:

```csharp
public interface IApiClient
{
static abstract string BasePath { get; }
}

public interface IFooClient : IApiClient
{
static string IApiClient.BasePath => "api/foo";
}

public static class GenericCaller
{
public static void PrintBasePath() where T : class, IApiClient
{
var path = T.BasePath; // throws BadImageFormatException
Console.WriteLine($"BasePath resolved to: {path}");
}
}
```

3. Call `GenericCaller.PrintBasePath()` from `Program.cs`
4. Run in the browser

### Minimal repro project

WasmStaticAbstractRepro.csproj

```xml


net10.0
enable
enable





```

Program.cs

```csharp
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using WasmStaticAbstractRepro;

var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add("#app");
builder.RootComponents.Add("head::after");

try
{
GenericCaller.PrintBasePath();
Console.WriteLine("SUCCESS: Static abstract dispatch worked.");
}
catch (Exception ex)
{
Console.WriteLine($"FAILED: {ex.GetType().Name}: {ex.Message}");
}

await builder.Build().RunAsync();

public interface IApiClient
{
static abstract string BasePath { get; }
}

public interface IFooClient : IApiClient
{
static string IApiClient.BasePath => "api/foo";
}

public static class GenericCaller
{
public static void PrintBasePath() where T : class, IApiClient
{
var path = T.BasePath;
Console.WriteLine($"BasePath resolved to: {path}");
}
}
```

App.razor, _Imports.razor, wwwroot/index.html

```razor
@* App.razor *@

WASM Static Abstract Repro


Check the browser console (F12) for results.


```

```razor
@* _Imports.razor *@
@using Microsoft.AspNetCore.Components.Web
```

```html



WASM Static Abstract Repro

Loading...

```

## Expected behavior

`T.BasePath` resolves to the explicit interface implementation and prints `"api/foo"`.

## Actual behavior

```
FAILED: BadImageFormatException: Method has no body
```

Without a try-catch, the error surfaces as `AggregateException_ctor_DefaultMessage (Method has no body)` in the browser console (because `UseSystemResourceKeys` is `true` in the WASM runtime config, making the error message particularly hard to diagnose).

## Workaround

Avoid dispatching the static abstract member through the generic type parameter. Pass the value as a regular parameter instead:

```csharp
public static void PrintBasePath(string basePath) where T : class
{
Console.WriteLine($"BasePath resolved to: {basePath}");
}

PrintBasePath("api/foo");
```

## Configuration

- .NET SDK: 10.0.103
- Runtime: 10.0.3
- Microsoft.NET.Sdk.WebAssembly.Pack: 10.0.3
- OS: macOS (Darwin 25.3.0, arm64)
- Browser: Reproduces in both Safari and Chromium (Edge)
- Blazor WebAssembly standalone (no AOT, no hosted model)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.