You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Apollo Federation: _entities resolver throws ObjectDisposedException in ResolverTask.CompleteUnsafeAsync under concurrency (15.1.x) #9971
It builds an in-process executor with one federated entity (a [Key("id")] type with a [ReferenceResolver] that awaits Task.Yield()), then fires the federation _entities query from 32 concurrent tasks × 10,000 iterations (320,000 total), forcing GC periodically so faulted tasks surface via the finalizer.
A TaskScheduler.UnobservedTaskException handler counts exceptions originating from ResolverTask.CompleteUnsafeAsync. Within ~320K requests on a multi-core machine, the handler captures the ObjectDisposedException. The race is statistical — re-run, or raise Iterations/Concurrency, if it does not fire on the first run on a slow or single-core machine.
What is expected?
The _entities query executes and returns normally with no unobserved task exceptions. Per-representation context clones created inside EntitiesResolver.ResolveAsync should complete their cleanup (clone.CompleteUnsafeAsync) without touching an OperationContext that has already been returned to the pool.
What is actually happening?
Under concurrency, the _entities resolver crashes during cleanup.
For each representation, EntitiesResolver clones the request context and queues the clone's cleanup to run when the parent resolver finishes. But the parent signals "done" to the scheduler before that cleanup runs. If _entities was the last work item, another thread sees "done" and recycles the OperationContext into the pool — so the cleanup then runs against a pooled context and throws ObjectDisposedException. No one awaits the faulted task, so it surfaces on the finalizer thread as an UnobservedTaskException.
Responses stay correct, but prod logs this ~8–11K times/day. Not reproducible on 16.1.4 (0 in 1M requests)
Relevant log output
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'The specified object was not initialized and is no longer usable.'.
at HotChocolate.Execution.Processing.WorkScheduler.Complete(IExecutionTask task)
at HotChocolate.Execution.Processing.Tasks.ResolverTask.CompleteUnsafeAsync()
at HotChocolate.Execution.Processing.MiddlewareContext.ExecuteCleanupTasksAsync()
at HotChocolate.Execution.Processing.Tasks.ResolverTask.ExecuteAsync(CancellationToken cancellationToken)
We are seeing this on 15.1.17 as well, through the Relay node(id:) field rather than _entities. NodeFieldResolvers clones the resolver context the same way EntitiesResolver does, so any Relay @refetchable fragment on a Node type hits the same race. The 15.1.18 tag and main-version-15 still have the old ordering in ResolverTask.Execute.cs. I opened #10364 with a cherry-pick of 6e5b640 onto main-version-15, and confirmed locally that the backported test fails on that branch without the fix and passes with it. Would you consider including it in the next 15.1.x patch release?
Activity
erf1nd0r commentedon Sep 7, 2026
We are seeing this on 15.1.17 as well, through the Relay
node(id:)field rather than_entities.NodeFieldResolversclones the resolver context the same wayEntitiesResolverdoes, so any Relay@refetchablefragment on aNodetype hits the same race. The 15.1.18 tag andmain-version-15still have the old ordering inResolverTask.Execute.cs. I opened #10364 with a cherry-pick of 6e5b640 ontomain-version-15, and confirmed locally that the backported test fails on that branch without the fix and passes with it. Would you consider including it in the next 15.1.x patch release?