Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 30, 2026

Background work (e.g., Kestrel server in SseServerIntegrationTestFixture) can continue logging after xUnit has disposed TestOutputHelper, causing NullReferenceException in TestOutputHelper.QueueTestOutput().

Changes

  • tests/Common/Utils/XunitLoggerProvider.cs: Catch InvalidOperationException and NullReferenceException around output.WriteLine() to gracefully handle logging after test disposal
try
{
    output.WriteLine(sb.ToString());
}
catch (InvalidOperationException)
{
    // Test has already completed
}
catch (NullReferenceException)
{
    // xUnit v3 internal queue torn down
}
Original prompt

This section details on the original issue you should resolve

<issue_title>ModelContextProtocol.AspNetCore.Tests.HttpServerIntegrationTests.Connect_TestServer_ShouldProvideServerFields test failed in CI</issue_title>
<issue_description>```
[xUnit.net 00:00:11.21] ModelContextProtocol.AspNetCore.Tests.SseServerIntegrationTests.Connect_TestServer_ShouldProvideServerFields [FAIL]
Failed ModelContextProtocol.AspNetCore.Tests.SseServerIntegrationTests.Connect_TestServer_ShouldProvideServerFields [73 ms]
Error Message:
System.Net.Http.HttpRequestException : An error occurred while sending the request.
---- System.Net.Http.HttpIOException : The response ended prematurely. (ResponseEnded)
Stack Trace:
at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at ModelContextProtocol.Client.McpHttpClient.SendAsync(HttpRequestMessage request, JsonRpcMessage message, CancellationToken cancellationToken) in //src/ModelContextProtocol.Core/Client/McpHttpClient.cs:line 22
at ModelContextProtocol.Client.StreamableHttpClientSessionTransport.SendHttpRequestAsync(JsonRpcMessage message, CancellationToken cancellationToken) in /
/src/ModelContextProtocol.Core/Client/StreamableHttpClientSessionTransport.cs:line 93
at ModelContextProtocol.Client.AutoDetectingClientSessionTransport.InitializeAsync(JsonRpcMessage message, CancellationToken cancellationToken) in //src/ModelContextProtocol.Core/Client/AutoDetectingClientSessionTransport.cs:line 69
at ModelContextProtocol.Client.AutoDetectingClientSessionTransport.InitializeAsync(JsonRpcMessage message, CancellationToken cancellationToken) in /
/src/ModelContextProtocol.Core/Client/AutoDetectingClientSessionTransport.cs:line 90
at ModelContextProtocol.McpSessionHandler.SendRequestAsync(JsonRpcRequest request, CancellationToken cancellationToken) in //src/ModelContextProtocol.Core/McpSessionHandler.cs:line 504
at ModelContextProtocol.McpSession.SendRequestAsync[TParameters,TResult](String method, TParameters parameters, JsonTypeInfo1 parametersTypeInfo, JsonTypeInfo1 resultTypeInfo, RequestId requestId, CancellationToken cancellationToken) in /
/src/ModelContextProtocol.Core/McpSession.Methods.cs:line 76
at ModelContextProtocol.Client.McpClientImpl.ConnectAsync(CancellationToken cancellationToken) in //src/ModelContextProtocol.Core/Client/McpClientImpl.cs:line 532
at ModelContextProtocol.Client.McpClientImpl.ConnectAsync(CancellationToken cancellationToken) in /
/src/ModelContextProtocol.Core/Client/McpClientImpl.cs:line 589
at ModelContextProtocol.Client.McpClient.CreateAsync(IClientTransport clientTransport, McpClientOptions clientOptions, ILoggerFactory loggerFactory, CancellationToken cancellationToken) in //src/ModelContextProtocol.Core/Client/McpClient.Methods.cs:line 53
at ModelContextProtocol.Client.McpClient.CreateAsync(IClientTransport clientTransport, McpClientOptions clientOptions, ILoggerFactory loggerFactory, CancellationToken cancellationToken) in /
/src/ModelContextProtocol.Core/Client/McpClient.Methods.cs:line 63
at ModelContextProtocol.AspNetCore.Tests.HttpServerIntegrationTests.Connect_TestServer_ShouldProvideServerFields() in //tests/ModelContextProtocol.AspNetCore.Tests/HttpServerIntegrationTests.cs:line 50
--- End of stack trace from previous location ---
----- Inner Stack Trace -----
at System.Net.Http.HttpConnection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
Standard Output Messages:
| [2026-01-30T20:10:35] ModelContextProtocol.Client.McpClient Debug: In-memory SSE Client sending method 'initialize' request.
| [2026-01-30T20:10:35] ModelContextProtocol.Client.AutoDetectingClientSessionTransport Debug: In-memory SSE Client attempting to connect using Streamable HTTP transport.
| [2026-01-30T20:10:35] Microsoft.AspNetCore.Server.Kestrel Warning: Connection processing ended abnormally.
System.AggregateException: An error occurred while writing to logger(s). (Object reference not set to an instance of an object.)
---> System.NullReferenceException: Object reference not set to an instance of an object.
at Xunit.v3.TestOutputHelper.QueueTestOutput(String output) in /
/src/xunit.v3.core/Framework/TestOutputHelper.cs:line 46
at Xunit.v3.TestOutputHelper.WriteLine(String message) in /_/src/xunit.v3.core/Frame...


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 2 commits January 30, 2026 22:34
Add try-catch in XunitLoggerProvider to gracefully handle exceptions when
logging after xUnit's TestOutputHelper has been disposed. This prevents
NullReferenceException when background work continues after test completion.

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Address code review feedback by catching specific exception types
(InvalidOperationException and NullReferenceException) instead of
catching all exceptions. This helps distinguish between expected
disposal-related exceptions and genuine errors.

Co-authored-by: stephentoub <2642209+stephentoub@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix test failure in Connect_TestServer_ShouldProvideServerFields Fix XunitLoggerProvider crash when background work logs after test disposal Jan 30, 2026
Copilot AI requested a review from stephentoub January 30, 2026 22:39
@stephentoub stephentoub marked this pull request as ready for review January 30, 2026 22:39
@stephentoub stephentoub requested a review from ericstj January 30, 2026 22:40
Copilot AI requested a review from stephentoub January 30, 2026 22:41
@stephentoub stephentoub enabled auto-merge (squash) January 30, 2026 22:45
@stephentoub stephentoub merged commit 50d6d0b into main Jan 30, 2026
11 checks passed
@stephentoub stephentoub deleted the copilot/fix-http-request-exception branch January 30, 2026 22:56
@ericstj
Copy link
Collaborator

ericstj commented Feb 2, 2026

@stephentoub I was working with this same issue locally with copilot CLI and it suggested the same fix -- just to swallow the exception. However I didn't propose the PR since it doesn't seem to me like the test infrastructure would ever let the kestrel instance live longer than a test. I couldn't find the place where we'd expect this to throw like this. For instance -- the stack in the issue shows a test is still running. Could you find such a place? I was thinking this might be a leak somewhere, or a flaw in the test infrastructure (around concurrency or lifetime).

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.

ModelContextProtocol.AspNetCore.Tests.HttpServerIntegrationTests.Connect_TestServer_ShouldProvideServerFields test failed in CI

4 participants