C# async void in a button click handler, the exception just disappears?
Jonas Becker@jonas_fiae
Hello, I have a question to you. In my Ausbildung company we have a WinForms tool (yes WinForms xD). I made a button that loads customers from our internal API. When the API is down, nothing happens. No error, no MessageBox, the grid just stays empty.
private async void btnLoad_Click(object sender, EventArgs e)
{
try
{
LoadData();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
private async Task LoadData()
{
var json = await _http.GetStringAsync("https://intern.example/api/kunden");
gridKunden.DataSource = JsonSerializer.Deserialize<List<Kunde>>(json);
}
The try catch is there but it does not catch anything. My Ausbilder says "never async void", but the event handler must be void?? How can I make that the error is shown in a MessageBox? .NET 8, Visual Studio 2022. Danke!
Accepted answer
Your Ausbilder is right, with one exception: event handlers. Their signature is fixed, so async void is allowed there and only there. The rule is async void for event handlers, async Task for everything else, and inside the handler you await everything.
Your bug is one missing word. You call LoadData() without await. It returns a Task, the exception gets stored inside that Task, and nobody ever looks at it. Your try/catch finished before the request even failed. Visual Studio told you, by the way: warning CS4014, "this call is not awaited".
private async void btnLoad_Click(object sender, EventArgs e)
{
btnLoad.Enabled = false;
try
{
await LoadData();
}
catch (HttpRequestException ex)
{
MessageBox.Show("API nicht erreichbar: " + ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
btnLoad.Enabled = true;
}
}
After the await you're back on the UI thread, so setting gridKunden.DataSource is safe. Don't sprinkle ConfigureAwait(false) in UI code before touching controls. And set _http.Timeout to something sensible, the default is 100 seconds, which feels like forever with a frozen button.
Last thing: turn on TreatWarningsAsErrors in the csproj. That warning list is a to-do list, not wallpaper.
Get the best of #compiling weekly
One email each Monday with the most useful posts. No spam; unsubscribe in one click.
Comments
Sign in to join the conversation.
Sign in to commentLena Hoffmann@arch_btw_lena
not a c# person but your ausbilder is right about async void 90% of the time. someone with .net brain will come. (also: WinForms in 2026, läuft bei euch)
Jonas Becker@jonas_fiae
danke Lena xD yes WinForms, the tool is from 2014 and nobody wants to touch it
Accepted answer
Gary Pritchard@contractor_gaz
Your Ausbilder is right, with one exception: event handlers. Their signature is fixed, so async void is allowed there and only there. The rule is async void for event handlers, async Task for everything else, and inside the handler you await everything.
Your bug is one missing word. You call LoadData() without await. It returns a Task, the exception gets stored inside that Task, and nobody ever looks at it. Your try/catch finished before the request even failed. Visual Studio told you, by the way: warning CS4014, "this call is not awaited".
private async void btnLoad_Click(object sender, EventArgs e)
{
btnLoad.Enabled = false;
try
{
await LoadData();
}
catch (HttpRequestException ex)
{
MessageBox.Show("API nicht erreichbar: " + ex.Message, "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
btnLoad.Enabled = true;
}
}
After the await you're back on the UI thread, so setting gridKunden.DataSource is safe. Don't sprinkle ConfigureAwait(false) in UI code before touching controls. And set _http.Timeout to something sensible, the default is 100 seconds, which feels like forever with a frozen button.
Last thing: turn on TreatWarningsAsErrors in the csproj. That warning list is a to-do list, not wallpaper.
Jonas Becker@jonas_fiae
It works!! MessageBox comes now when the API is down. And I see the CS4014 warning now, it was there all the time lol. TreatWarningsAsErrors is on. Thank you Gaz!
Tan Wei Jie@weijie_jb
wah WinForms 2014, same like my company. the back on the UI thread part is important, our junior put ConfigureAwait(false) everywhere after reading one blog and the grid threw cross-thread exceptions lol
Imran Hussain@imran_builds
Gaz giving patient answers to apprentices, I'm screenshotting this for the next time he's mardy in the politics threads 😄