Skip to content

Configure an await to propagate all errors by throwing an AggregateException #47605

@theodorzoulias

Description

@theodorzoulias

Let's throw another marginally useful idea for configuring awaits.

Background and Motivation

The await operator currently propagates only the first of the exceptions stored in a Task object. This is convenient in the vast majority of cases, because most asynchronous APIs by design will never complete with more than one errors. There is a handful of APIs though that do propagate multiple exceptions quite normally, and most of this information is lost when these APIs are awaited in the standard way. Although it is possible for a programmer to observe and handle all errors in this case, the workaround needed is slightly cumbersome and error prone.

Proposed API

A new value ThrowAggregateException for the AwaitBehavior enum, which serves as a parameter for the ConfigureAwait method. This is an extension of the #47525 proposal.

namespace System.Threading.Tasks { [Flags] public enum AwaitBehavior { //... ThrowAggregateException = 0x8, // Instead of throwing only the first exception } }

Usage Examples

This configuration could be used when awaiting the Task.WhenAll method, the upcoming Parallel.ForEachAsync method, and the Completion property of the TPL Dataflow blocks:

try { await Task.WhenAll(tasks).ConfigureAwait(AwaitBehavior.ThrowAggregateException); } catch (AggregateException aex) { aex.Handle(ex => { /* ... */ }); }

The current workaround is to store the task in a variable before awaiting it, and in case of
exception to query the task's Exception property:

var whenAllTask = Task.WhenAll(tasks); try { await whenAllTask; } catch (Exception ex) { if (whenAllTask.IsFaulted) { whenAllTask.Exception.Handle(ex => { /* ... */ }); } else { // handle ex, which is certainly an OperationCanceledException } }

Alternative Designs

An extension method WithAggregateException has been suggested in a StackOverflow question:

public static async Task WithAggregateException(this Task source) { try { await source.ConfigureAwait(false); } catch { if (source.Exception == null) throw; ExceptionDispatchInfo.Capture(source.Exception).Throw(); } }

Risks

None that I can think of.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions