Fix XunitLoggerProvider crash when background work logs after test disposal#1229
Fix XunitLoggerProvider crash when background work logs after test disposal#1229stephentoub merged 3 commits intomainfrom
Conversation
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>
|
@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). |
|
@ericstj For the vast majority of the Kestrel-based tests using KestrelInMemoryTest, you're right. However, the SseServerIntegrationTestFixture used by the HttpServerIntegrationTests-derived tests outlives the ITestOutputHelper. The better fix might be to update all the tests using the SseServerIntegrationTestFixture to be a |
Background work (e.g., Kestrel server in
SseServerIntegrationTestFixture) can continue logging after xUnit has disposedTestOutputHelper, causingNullReferenceExceptioninTestOutputHelper.QueueTestOutput().Changes
tests/Common/Utils/XunitLoggerProvider.cs: CatchInvalidOperationExceptionandNullReferenceExceptionaroundoutput.WriteLine()to gracefully handle logging after test disposalOriginal 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, JsonTypeInfo
1 parametersTypeInfo, JsonTypeInfo1 resultTypeInfo, RequestId requestId, CancellationToken cancellationToken) in //src/ModelContextProtocol.Core/McpSession.Methods.cs:line 76at 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.