-
Notifications
You must be signed in to change notification settings - Fork 29
System.ObjectDisposedException when the request is already disposed #223
Description
Describe the bug
Below is the logs when I have verbose logging turned on:
[18:56:04 WARN JunimoServer] Error handling request: System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.HttpListenerResponse'.
at System.Net.HttpListenerResponse.CheckDisposed()
at System.Net.HttpListenerResponse.set_StatusCode(Int32 value)
at JunimoServer.Services.Api.ApiService.HandleRequestAsync(HttpListenerContext context) in /src/mod/JunimoServer/Services/Api/ApiService.cs:line 654
[18:56:04 WARN JunimoServer] Failed to write error response: System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.HttpListenerResponse'.
at System.Net.HttpListenerResponse.CheckDisposed()
at System.Net.HttpListenerResponse.set_StatusCode(Int32 value)
at JunimoServer.Services.Api.ApiService.HandleRequestAsync(HttpListenerContext context) in /src/mod/JunimoServer/Services/Api/ApiService.cs:line 684
[18:57:10 WARN JunimoServer] Error handling request: System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.HttpListenerResponse'.
at System.Net.HttpListenerResponse.CheckDisposed()
at System.Net.HttpListenerResponse.set_StatusCode(Int32 value)
at JunimoServer.Services.Api.ApiService.HandleRequestAsync(HttpListenerContext context) in /src/mod/JunimoServer/Services/Api/ApiService.cs:line 654
[18:57:10 WARN JunimoServer] Failed to write error response: System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'System.Net.HttpListenerResponse'.
at System.Net.HttpListenerResponse.CheckDisposed()
at System.Net.HttpListenerResponse.set_StatusCode(Int32 value)
at JunimoServer.Services.Api.ApiService.HandleRequestAsync(HttpListenerContext context) in /src/mod/JunimoServer/Services/Api/ApiService.cs:line 684
Steps to reproduce
I have not tried to reproduce myself, I would assume something along the lines of:
- keep the server running
- send a WebSocket request to the API endpoint and then immediately terminate it
Because the WebSocket request fails and we dispose it, any attempt later in catch and finally block to assign status code to the response will result in a ObjectDisposedException.
Expected behavior
Expected a log to be printed, but no an uncaught exception.
Additional context
The explanation below is generated by AI, but I have verified its correctness.
The issue is in the HandleRequestAsync method's error handling flow when a WebSocket upgrade request comes in on the /ws path.
Root Cause
Look at this sequence in HandleRequestAsync:
stardew-valley-dedicated-server / server / mod / JunimoServer / Services / Api / ApiService.cs
v1
// Handle WebSocket upgrade
if (path == "/ws" && request.IsWebSocketRequest)
{
await HandleWebSocketAsync(context);
return;
}
When a WebSocket connection is established via HandleWebSocketAsync, the HTTP response is consumed and disposed as part of the WebSocket upgrade handshake (context.AcceptWebSocketAsync). The method then returns.
However, the finally block always runs:
finally
{
try { response.Close(); }
catch (Exception closeEx)
{
Monitor.Log($"Failed to close response: {closeEx}", LogLevel.Debug);
The response.Close() call on an already-disposed response object throws ObjectDisposedException. That exception is caught and logged at Debug level — but that's not what's causing the WARN logs.
The real problem is: if HandleWebSocketAsync itself throws an exception (e.g., the client disconnects abruptly during the upgrade, or AcceptWebSocketAsync fails), execution falls into the outer catch block:
catch (Exception ex)
{
Monitor.Log($"Error handling request: {ex}", LogLevel.Warn); // ← line ~654 in stack
try
{
response.StatusCode = 500; // ← ObjectDisposedException!
At this point the response has already been disposed by the WebSocket upgrade, so setting response.StatusCode = 500 throws ObjectDisposedException, which triggers the inner catch — producing both WARN log lines you see.
Final checks
- Read the contribution guide.
- Check existing issues.