Skip to content

Fix ObjectDisposedException by awaiting handler tasks before session disposal#1401

Open
Varun6578 wants to merge 1 commit intomodelcontextprotocol:mainfrom
Varun6578:fix/await-handler-tasks-before-scope-disposal
Open

Fix ObjectDisposedException by awaiting handler tasks before session disposal#1401
Varun6578 wants to merge 1 commit intomodelcontextprotocol:mainfrom
Varun6578:fix/await-handler-tasks-before-scope-disposal

Conversation

@Varun6578
Copy link

Summary

Fixes #1269

When using HTTP transport with Stateless = true, if the client closes the connection (e.g., OpenAI times out on a long-running tool), the ASP.NET Core request scope is disposed while tool handlers are still executing, causing ObjectDisposedException.

Root Cause

In McpSessionHandler.ProcessMessagesCoreAsync(), message handlers were launched as fire-and-forget tasks (_ = ProcessMessageAsync()). These tasks were not tracked or awaited.

When McpSessionHandler.DisposeAsync() cancelled the message processing CTS and awaited _messageProcessingTask, the channel reader loop exited immediately but the fire-and-forget handler tasks continued running. The session disposal then completed, ASP.NET Core disposed the request service scope, and the still-running handlers got ObjectDisposedException when resolving scoped services.

Fix

  • Track all handler tasks in a List<Task> instead of discarding them
  • await Task.WhenAll(pendingHandlerTasks) in the finally block of ProcessMessagesCoreAsync before the method returns
  • Periodically prune completed tasks to avoid unbounded list growth
  • Added a regression test verifying scoped services remain accessible throughout a delayed handler's lifetime

Test Results

  • Build: 0 warnings, 0 errors
  • Core tests: 1838 passed (net10.0), 1838 passed (net9.0), 1654 passed (net472)
  • ASP.NET Core tests: 302 passed (2 pre-existing conformance failures unrelated to this change)

…disposal

Track fire-and-forget message handler tasks in ProcessMessagesCoreAsync
and await them in the finally block. This ensures that when the message
processing task completes (e.g., due to cancellation), all outstanding
handler tasks have finished before the method returns. Since
McpSessionHandler.DisposeAsync awaits the message processing task, this
guarantees that service scopes are not disposed while handlers are still
executing.

This fixes an issue in stateless HTTP mode where ASP.NET Core disposes
the request service scope after the message processing task completes,
but previously fire-and-forget handler tasks could still be running,
causing ObjectDisposedException when they try to resolve scoped services.

Fixes modelcontextprotocol#1269

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ObjectDisposedException - IServiceProvider disposed while tool handler execution.

1 participant