Skip to content

[Breaking change]: IHost.RunAsync and IHost.StopAsync throw when a BackgroundService fails with an exception #52290

@svick

Description

@svick

Description

When a Microsoft.Extensions.Hosting.BackgroundService running in the default Microsoft.Extensions.Hosting.IHost throws an exception from its ExecuteAsync method and HostOptions.BackgroundServiceExceptionBehavior is set to BackgroundServiceExceptionBehavior.StopHost (the default), the host is stopped. As a result, awaiting RunAsync or StopAsync on the host completes and the application typically exits.

Previously, awaiting RunAsync or StopAsync on the host completed successfully and this usually meant the application exited with a success exit code (zero).

Now, awaiting RunAsync or StopAsync on the host will throw an exception, which will usually mean that the application will exit with a failure exit code (non-zero).

Version

.NET 11 Preview 3

Previous behavior

When a BackgroundService throws an exception from its ExecuteAsync method and HostOptions.BackgroundServiceExceptionBehavior is set to BackgroundServiceExceptionBehavior.StopHost, the task returned from calling RunAsync , StopAsync or WaitForShutdownAsync on the default host completes successfully.

New behavior

When a BackgroundService throws an exception from its ExecuteAsync method and HostOptions.BackgroundServiceExceptionBehavior is set to BackgroundServiceExceptionBehavior.StopHost, the task returned from calling RunAsync, StopAsync or WaitForShutdownAsync on the default host fails with an exception. If a single service fails, the exception thrown by the service is rethrown. If multiple services fail, their exceptions are combined into an AggregateException.

Type of breaking change

  • Binary incompatible: Existing binaries might encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code might require source changes to compile successfully.
  • Behavioral change: Existing binaries might behave differently at run time.

Reason for change

If an application stops because of a failure, the application shouldn't exit with a success exit code. The previous behavior was effectively hiding the failure, making it harder to detect and diagnose the issue that caused the exception.

Recommended action

The recommended action is to do nothing: a failing application should exit with a failure exit code, which is the new behavior.

If it is necessary to maintain the previous behavior (success exit code), wrap the await of RunAsync or StopAsync on the host in a try-catch block.

Feature area

Extensions

Affected APIs

IHost.StopAsync with the default implementation of IHost and any methods that call it:

  • HostingAbstractionsHostExtensions.RunAsync
  • HostingAbstractionsHostExtensions.Run
  • HostingAbstractionsHostExtensions.StopAsync
  • HostingAbstractionsHostExtensions.WaitForShutdownAsync
  • HostingAbstractionsHostExtensions.WaitForShutdown

Further information

The relevant PR is dotnet/runtime#124863 and the issue is dotnet/runtime#67146.


Associated WorkItem - 560726

Metadata

Metadata

Assignees

Labels

📌 seQUESTeredIdentifies that an issue has been imported into Quest.breaking-changeIndicates a .NET Core breaking change

Type

No type

Projects

Status

🔖 Ready

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions