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);