Found in the pre-4.0 audit (follow-up to #360/#361).
AsyncEnumerableWorkerPool.ThrowIfFailed throws only the first queued exception; the rest of the ConcurrentQueue<Exception> is discarded entirely — not aggregated, not reachable via any task's Exception property.
Contrast with the enumerable paths, which preserve every failure: processor WhenAll tasks carry all faults, and ParallelExtensions completes its TCS with the full exception set.
Failure scenario: asyncItems.ForEachAsync(f).ProcessInParallel(maxConcurrency: 4).ExecuteAsync() where 5 of 10 items throw different exceptions → caller observes exactly 1; the other 4 are gone. Same fluent call on an IEnumerable<T> source surfaces all 5.
Related: faulted per-item completion sources abandoned after the first failure propagates in ProcessResultsAsync used to raise UnobservedTaskException at GC — the consumer-side observation added in #360 covers the bounded result path, but the void path's discarded queue remains.
Proposal: throw AggregateException when more than one failure is queued (matches Task.WhenAll expectations), or at minimum document the first-wins contract. Decide before 4.0 if the fix is considered breaking.
Found in the pre-4.0 audit (follow-up to #360/#361).
AsyncEnumerableWorkerPool.ThrowIfFailedthrows only the first queued exception; the rest of theConcurrentQueue<Exception>is discarded entirely — not aggregated, not reachable via any task'sExceptionproperty.Contrast with the enumerable paths, which preserve every failure: processor
WhenAlltasks carry all faults, andParallelExtensionscompletes its TCS with the full exception set.Failure scenario:
asyncItems.ForEachAsync(f).ProcessInParallel(maxConcurrency: 4).ExecuteAsync()where 5 of 10 items throw different exceptions → caller observes exactly 1; the other 4 are gone. Same fluent call on anIEnumerable<T>source surfaces all 5.Related: faulted per-item completion sources abandoned after the first failure propagates in
ProcessResultsAsyncused to raiseUnobservedTaskExceptionat GC — the consumer-side observation added in #360 covers the bounded result path, but the void path's discarded queue remains.Proposal: throw
AggregateExceptionwhen more than one failure is queued (matchesTask.WhenAllexpectations), or at minimum document the first-wins contract. Decide before 4.0 if the fix is considered breaking.