From da62ca82fea20ae8f3621425a117511843b886fc Mon Sep 17 00:00:00 2001 From: Mahmoud Salem Date: Tue, 7 Jul 2026 15:53:29 +0300 Subject: [PATCH] fix(api): treat client request-aborts as 499, not a logged 500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A cancelled HTTP request (the client disconnects, or React Query aborts an in-flight query on unmount/refetch) makes ASP.NET's RequestAborted token fire and the in-flight EF query throw OperationCanceledException. It was falling into the catch-all — logged as "Unhandled exception" and answered with a 500 to a socket that's already gone. Catch it explicitly (guarded on RequestAborted) and return 499 without the error log. Expected behaviour, not an error. --- .../Middleware/ExceptionHandlerMiddleware.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/server/src/ScholarPath.API/Middleware/ExceptionHandlerMiddleware.cs b/server/src/ScholarPath.API/Middleware/ExceptionHandlerMiddleware.cs index cf32758e..f5db967b 100644 --- a/server/src/ScholarPath.API/Middleware/ExceptionHandlerMiddleware.cs +++ b/server/src/ScholarPath.API/Middleware/ExceptionHandlerMiddleware.cs @@ -82,6 +82,19 @@ await WriteProblemAsync( "Service unavailable", suex.Message).ConfigureAwait(false); } + catch (OperationCanceledException) when (context.RequestAborted.IsCancellationRequested) + { + // The client disconnected / aborted the request (e.g. React Query + // cancels an in-flight query on unmount or refetch), so ASP.NET's + // RequestAborted token fired and cancelled the EF query. This is + // expected, not an error — don't log it as "unhandled" or try to write + // a 500 to a socket that's already gone. 499 = client closed request. + logger.LogDebug("Request aborted by the client at {Path}", context.Request.Path); + if (!context.Response.HasStarted) + { + context.Response.StatusCode = 499; + } + } catch (Exception ex) { logger.LogError(ex, "Unhandled exception at {Path}", context.Request.Path);