Skip to content

Awaiting nongeneric Task.WhenAll changes behavior in .NET 8 to throw the last exception instead of the first #93504

@jnm2

Description

@jnm2

Description

The WhenAll awaiter has always thrown the exception from the first faulting task only, where 'first' refers to the order within the collection of tasks passed to WhenAll, rather than to the order in which the exceptions were thrown.

This changes in .NET 8, and it only changes for nongeneric Task.WhenAll. Generic Task.WhenAll retains the behavior that WhenAll has always had.

There is no mention of this in https://learn.microsoft.com/en-us/dotnet/core/compatibility/8.0#core-net-libraries.

Was this intentional, or perhaps an accidental side effect during #88154 or other recent performance improvements? /cc @stephentoub

Reproduction Steps

var ex1 = new Exception();
var ex2 = new Exception();

try
{
    var source1 = new TaskCompletionSource<object>();
    var source2 = new TaskCompletionSource<object>();

    // If the (Task) cast is removed, the behavior changes back to .NET 7 behavior.
    var whenAllTask = Task.WhenAll((Task)source1.Task, source2.Task);

    source2.SetException(ex2);
    source1.SetException(ex1);

    await whenAllTask;
}
catch (Exception ex)
{
    Console.WriteLine(ReferenceEquals(ex, ex1)); // False on .NET 8, true on .NET 7.
}

Expected behavior

The exception thrown during the await continuing to be the exception from the first task, as in all prior versions of .NET.

Actual behavior

The exception thrown during the await is the exception from the last task.

Regression?

No response

Known Workarounds

No response

Configuration

Used .NET SDK 8.0.100-rc.2.23502.2.

Other information

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions