Skip to content

WebApplicationFactory sometimes throws ObjectDisposedException instead of the app's startup exception #69684

Description

@Demivan

Is there an existing issue for this?

  • I have searched the existing issues

Describe the bug

When the app under test fails in IHost.StartAsync (for example ValidateOnStart throwing OptionsValidationException), WebApplicationFactory<T>.CreateClient() is supposed to rethrow that exception. Intermittently it throws ObjectDisposedException: Cannot access a disposed object. Object name: 'IServiceProvider' from DeferredHostBuilder.DeferredHost.StartAsync instead, and the real startup error is lost.

It's a race between two threads:

  1. HostFactoryResolver runs the app's entry point on its own thread with stopApplication: false. builder.Build() hands the host to the factory, and the entry point carries on into app.Run().
  2. StartAsync throws, and the finally in HostingAbstractionsHostExtensions.RunAsync disposes the host. After that, the entry point exits and DeferredHostBuilder.EntryPointCompleted(exception) faults the start TCS with the real exception.
  3. Meanwhile, on the test thread, DeferredHost.StartAsync calls _host.Services.GetRequiredService<IHostApplicationLifetime>() to register on ApplicationStarted. If step 2 has already disposed the host, this throws ObjectDisposedException before the TCS is ever awaited.

In our CI it hits about 1 in 16 runs on busy x64 Linux agents, and never on an arm64 Mac.

Expected Behavior

CreateClient() consistently throws the exception the app failed to start with (OptionsValidationException here), as it already does when the app throws before Run().

Steps To Reproduce

Widening the window with an overridden CreateHost makes it fail every time:

// Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddOptions<MyOptions>().Validate(_ => false, "invalid").ValidateOnStart();
var app = builder.Build();
app.Run();

public class MyOptions { }
public partial class Program { }
// Test
public class StartupFailureTests
{
    [Fact]
    public void CreateClient_ThrowsStartupException()
    {
        using var factory = new SlowStartFactory();
        Assert.Throws<OptionsValidationException>(() => factory.CreateClient());
        // Actual: ObjectDisposedException
    }

    private sealed class SlowStartFactory : WebApplicationFactory<Program>
    {
        protected override IHost CreateHost(IHostBuilder builder)
        {
            var host = builder.Build();
            Thread.Sleep(500); // let the app thread fail and dispose the host first
            host.Start();
            return host;
        }
    }
}

Without the override, the same test fails intermittently under CPU load.

Exceptions (if any)

System.ObjectDisposedException: Cannot access a disposed object. Object name: 'IServiceProvider'.
   at ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
   at Microsoft.AspNetCore.Mvc.Testing.DeferredHostBuilder.DeferredHost.StartAsync(CancellationToken)
   at HostingAbstractionsHostExtensions.Start(IHost host)
   at WebApplicationFactory`1.CreateHost(IHostBuilder builder)
   at WebApplicationFactory`1.ConfigureHostBuilder(IHostBuilder hostBuilder)
   at WebApplicationFactory`1.StartServer()
   at WebApplicationFactory`1.CreateClient()

.NET Version

10.0

Anything else?

  • ASP.NET Core version: Microsoft.AspNetCore.Mvc.Testing 10.0.12. DeferredHostBuilder is unchanged on main.
  • Related: Quarantine WebApplicationFactory #58442 reports the same ObjectDisposedException from other call sites (HostingListener.CreateHost, and the IServer lookup in WebApplicationFactory.ConfigureHostBuilder). The fix proposed here only covers the DeferredHost.StartAsync stack and doesn't address those.
  • Related: DeferredHostBuilder starts the host during the build action #38335. A July 2023 comment there reports this exact failure (ValidateOnStart failing in app.Run(), ObjectDisposedException at DeferredHostBuilder.cs line 152), worked around with test retries. That issue is about the broader, by-design behaviour of DeferredHostBuilder.Build() running the entry point on its own thread; the fix proposed here doesn't change that.

Proposed fix: in DeferredHost.StartAsync, if resolving IHostApplicationLifetime throws ObjectDisposedException, skip the ApplicationStarted registration and still await the start TCS. The host is only disposed once the entry point is on its way out, and HostFactoryResolver calls entrypointCompleted in a finally, so the TCS always completes with the app's exception (or with success). PR with a deterministic regression test: #69685.

Activity

  1. github-actions commented on Oct 6, 2026

    @github-actions
    Contributor

    Triage Summary

    Area: area-mvc (Microsoft.AspNetCore.Mvc.Testing — WebApplicationFactory / DeferredHostBuilder)
    Type: Bug (CreateClient throws ObjectDisposedException instead of the app's startup exception)

    Potential Duplicates

    • Quarantine WebApplicationFactory #58442 - Quarantine WebApplicationFactory (similarity: medium; reports the same ObjectDisposedException: IServiceProvider from other call sites, as the reporter notes)

    Notes

    Generated by Issue Triage Agent for dotnet/aspnetcore for #69684 · copilot · auto · 28.5 AIC · ⌖ 11.7 AIC · ⊞ 30.4K · ◷

  2. MihuBot commented on Oct 6, 2026

    @MihuBot

    I'm a bot. Here is a possible related and/or duplicate issue (I may be wrong):

  3. added
    area-mvcIncludes: MVC, Actions and Controllers, Localization, CORS, most templates
    on Oct 7, 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

    area-mvcIncludes: MVC, Actions and Controllers, Localization, CORS, most templates

    Type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions