BackgroundService Gotcha: Silent Failures
• CommentsBackgroundService Gotcha: Silent Failures
I know last time I talked about BackgroundService
… I don’t want to make this a series or anything, but there is another common “gotcha” when it comes to BackgroundService
: exceptions are silently ignored.
If the ExecuteAsync
implementation throws an exception, that exception is silently swallowed and ignored. This is because BackgroundService
captures the task from ExecuteAsync
but never await
s it - i.e., BackgroundService
uses fire-and-forget.
Update: .NET 6 has changed this behavior. By default, exceptions thrown from BackgroundService.ExecuteAsync
now log the exception and shut down the host. This blog post is preserved for historical reasons.
Problem Description
This problem will surface as BackgroundService
instances just stopping, without any indication of a problem. What actually happens if ExecuteAsync
throws an exception is that the exception is captured and placed on the Task
that was returned from ExecuteAsync
. The problem is that BackgroundService
doesn’t observe that task, so there’s no logging and no process crash - the BackgroundService
has completed executing but it just sits there doing nothing.
This is not necessarily a problem with BackgroundService
; fire-and-forget can be appropriate for “top-level” loops such as a background worker task. However, it would be nice to have logging at least, so this “gotcha” is detectable.
Solution
All top-level loops should have a try
/catch
with some kind of reporting if something goes wrong. ExecuteAsync
implementations are top-level loops, so they should have a top-level try
that catches all exceptions:
I recommend you combine this solution with the solution from last time that uses Task.Run
to avoid startup problems: