Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

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

Open Beginner friendly
#69,684 2 comments 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

@Demivan is already working on this.

Since Oct 6, 2026.

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
85/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
csharp
Domain
testing

Research direction

The bug resides in DeferredHostBuilder.DeferredHost.StartAsync in the ASP.NET Core Mvc.Testing library. Start by opening that method, where _host.Services.GetRequiredService<IHostApplicationLifetime>() is called to register an ApplicationStarted callback. Wrap that service resolution in a try/catch for ObjectDisposedException: if caught, skip the callback registration and continue to await the start TaskCompletionSource, which will already be faulted with the app's real startup exception. Run the regression test from linked PR #69685 to confirm CreateClient() consistently throws the original startup error instead of the ObjectDisposedException.

Written by the indexing model from the issue text.

Description

area-mvc

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: #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: #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.

Dominant language
C#
Stars
38.5k
Forks
13.8k
Avg merge
2d 2h
Merged PRs (30d)
221

Getting set up

Open in Codespaces

Starts the project's dev container in your browser, under your own GitHub account.

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from dotnet/aspnetcore

All issues in dotnet/aspnetcore

Similar issues

More C# issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.