Skip to content

Microsoft.AspNetCore.SystemWebAdapters.CoreServices 2.3.0 crashes on Linux with .NET 10 — IIISEnvironmentFeature removed from shared runtime #687

Description

@raviraj-ntp

What happened

I upgraded my web application from .NET 8 to .NET 10 and deployed it on RHEL 9. The service crashes immediately at startup before handling a single request. It was working fine on .NET 8 with package version 1.4.0.

IIISEnvironmentFeature was removed from the Linux copy of Microsoft.AspNetCore.Server.IIS.dll in .NET 10. SystemWebAdapters.CoreServices.dll still references it. When AddSystemWebAdapters() registers the assembly as an MVC application part, ControllerFeatureProvider calls GetDefinedTypes() on it at startup — and crashes because the type no longer exists in the shared runtime.

Exception

Unhandled exception. System.Reflection.ReflectionTypeLoadException:
  Unable to load one or more of the requested types.
  Could not load type 'Microsoft.AspNetCore.Server.IIS.IIISEnvironmentFeature'
  from assembly 'Microsoft.AspNetCore.Server.IIS, Version=10.0.0.0,
  Culture=neutral, PublicKeyToken=adb9793829ddae60'.
   at System.Reflection.RuntimeModule.GetDefinedTypes()
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerFeatureProvider.PopulateFeature(...)
   at Microsoft.AspNetCore.Mvc.ApplicationParts.ApplicationPartManager.PopulateFeature[TFeature](...)
   at Microsoft.AspNetCore.Mvc.ApplicationModels.ControllerActionDescriptorProvider.GetControllerTypes()
   at Microsoft.AspNetCore.Builder.ControllerEndpointRouteBuilderExtensions.MapControllers(...)
   at Program.<Main>$(String[] args)

How to reproduce

  1. Create a net10.0 ASP.NET Core web app.
  2. Reference Microsoft.AspNetCore.SystemWebAdapters.CoreServices 2.3.0.
  3. Call builder.Services.AddSystemWebAdapters() and app.UseSystemWebAdapters() unconditionally.
  4. Call app.MapControllers().
  5. Run on Linux with Microsoft.AspNetCore.App 10.0.8.

Process dies at startup with the exception above.

What I checked

I ran strings against the runtime DLL to confirm the type is actually gone — this isn't a load path issue:

$ strings /usr/lib64/dotnet/shared/Microsoft.AspNetCore.App/10.0.8/Microsoft.AspNetCore.Server.IIS.dll \
    | grep IIISEnvironmentFeature
# no output

COREHOST_TRACE confirms the assembly is coming straight from the shared framework, not a local copy:

Adding tpa entry: /usr/lib64/dotnet/shared/Microsoft.AspNetCore.App/10.0.8/Microsoft.AspNetCore.Server.IIS.dll,
  AssemblyVersion: 10.0.0.0, FileVersion: 10.0.826.23019

Workaround

Option 1 — OS guard

Sufficient when SystemWebAdapters.CoreServices is the only assembly referencing the removed type.

AddSystemWebAdapters() is what registers SystemWebAdapters.CoreServices.dll as an MVC application part.
Wrapping with an OS check prevents the registration on Linux, so GetDefinedTypes() is never called on it:

if (OperatingSystem.IsWindows())
{
    builder.Services.AddSystemWebAdapters();
}

// ...

if (OperatingSystem.IsWindows())
{
    app.UseSystemWebAdapters();
}

Option 2 — Resilient ControllerFeatureProvider

Needed when other assemblies in the project (e.g. pre-compiled DLLs built against .NET 8) also carry a reference to IIISEnvironmentFeature.

Replace ControllerFeatureProvider with an implementation that catches ReflectionTypeLoadException during type scanning.

Important: this must run after all AddMvc / AddControllersWithViews calls, because each of those internally checks OfType<ControllerFeatureProvider>().Any() and re-adds the default one if none is found.

builder.Services.AddControllersWithViews()
    .ConfigureApplicationPartManager(manager =>
    {
        var toRemove = manager.FeatureProviders
            .OfType<ControllerFeatureProvider>()
            .ToList();
        foreach (var fp in toRemove)
            manager.FeatureProviders.Remove(fp);
        manager.FeatureProviders.Add(new ResilientControllerFeatureProvider());
    });

ResilientControllerFeatureProvider must inherit ControllerFeatureProvider (so the internal Any() guard does not re-add the original) and use explicit interface implementation so ApplicationPartManager dispatches to the resilient PopulateFeature override instead of the base class method:

internal sealed class ResilientControllerFeatureProvider
    : ControllerFeatureProvider, IApplicationFeatureProvider<ControllerFeature>
{
    void IApplicationFeatureProvider<ControllerFeature>.PopulateFeature(
        IEnumerable<ApplicationPart> parts, ControllerFeature feature)
    {
        foreach (var part in parts.OfType<IApplicationPartTypeProvider>())
        {
            IEnumerable<TypeInfo> types;
            try
            {
                types = part.Types.ToList();
            }
            catch (ReflectionTypeLoadException ex)
            {
                types = ex.Types
                    .Where(t => t is not null)
                    .Select(t => t!.GetTypeInfo());
            }

            foreach (var typeInfo in types)
            {
                try
                {
                    if (IsController(typeInfo) && !feature.Controllers.Contains(typeInfo))
                        feature.Controllers.Add(typeInfo);
                }
                catch { }
            }
        }
    }
}

The problem

The package lists net10.0 as a supported TFM on NuGet, but it references a type that no longer exists in the .NET 10 Linux shared runtime. Either:

  • the NuGet compatibility metadata should be restricted to net10.0-windows, or
  • the reference to IIISEnvironmentFeature inside the package should be guarded so it does not cause reflection-based scanning to fail on non-Windows platforms.

Environment

Field Value
OS RHEL 9 x64
.NET runtime Microsoft.AspNetCore.App 10.0.8
.NET host 10.0.8, commit 94ea82652c, path /usr/lib64/dotnet/dotnet
Broken package Microsoft.AspNetCore.SystemWebAdapters.CoreServices 2.3.0
Also referenced Microsoft.AspNetCore.SystemWebAdapters 2.3.0
Last known good Microsoft.AspNetCore.SystemWebAdapters.CoreServices 1.4.0 on .NET 8 / Linux

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions