Client-driven request aborts/cancellations are logged at Error level and returned as HTTP 500 (and marked as errors on the Jaeger span). These are not server faults — the client disconnected or canceled mid-request — but they pollute the Error stream in Seq (the same stream used for alerting/triage) and inflate the API's error/500 metrics. Low functional impact, real observability impact.
System.OperationCanceledException: The operation was canceled.
at MongoDB.EntityFrameworkCore.Query.QueryingEnumerable... MoveNextAsync()
at ...ToListAsync[TSource](IQueryable, CancellationToken)
SpikerSoft.Api.Middleware.GlobalExceptionHandlerMiddleware — Unhandled exception ... POST /api/authentication/check-email (UserId: anonymous), exception:
System.IO.IOException: The client reset the request stream.
at ...HttpRequestPipeReader.ReadAsync(...) (request body read aborted)
Both are the caller going away — OperationCanceledException tied to HttpContext.RequestAborted, and the Kestrel "client reset the request stream" IOException.
Root cause (code)
SpikerSoft.Api/Middleware/GlobalExceptionHandlerMiddleware.cs:33 — catch (Exception ex) handles everything the same way: LogError (line ~75), sets Response.StatusCode = 500 (line ~86), and marks the current Activity/span as error (activity.SetStatus(Error), error=true). No special-casing for cancellation/client-abort.
In both catch blocks, detect client-abort and treat it as non-error:
if(exisOperationCanceledExceptionorTaskCanceledException&&context.RequestAborted.IsCancellationRequested){_logger.LogInformation("Request aborted by client: {Path}",context.Request.Path);context.Response.StatusCode=499;// client closed request (nginx convention)return;// do NOT mark the span as error}// also treat IOException "client reset the request stream" (Kestrel BadHttpRequest/connection reset) as client-abort
For TracingBehavior, log OperationCanceledException where the token is canceled at Debug/Information and skip the activity.SetStatus(Error) so canceled requests don't show as failed traces.
Impact of fixing
Cleaner Seq Error stream (fewer false positives during triage/alerting).
Accurate 5xx/error-rate metrics and Jaeger span success rates.
Priority: low (cosmetic/observability; ~6 events/6h currently), but worth doing since we rely on the error stream to find real issues.
Filed proactively by automated health check (Seq centralized error audit).
## Summary
Client-driven request **aborts/cancellations are logged at `Error` level and returned as HTTP 500** (and marked as errors on the Jaeger span). These are not server faults — the client disconnected or canceled mid-request — but they pollute the Error stream in Seq (the same stream used for alerting/triage) and inflate the API's error/500 metrics. Low functional impact, real observability impact.
## Evidence (Seq, last 6h)
- `SpikerSoft.Business.Behaviors.TracingBehavior` — `Query failed: GetLinkCommentsQuery` (4 events), exception:
```
System.OperationCanceledException: The operation was canceled.
at MongoDB.EntityFrameworkCore.Query.QueryingEnumerable... MoveNextAsync()
at ...ToListAsync[TSource](IQueryable, CancellationToken)
```
- `SpikerSoft.Api.Middleware.GlobalExceptionHandlerMiddleware` — `Unhandled exception ... POST /api/authentication/check-email` (UserId: anonymous), exception:
```
System.IO.IOException: The client reset the request stream.
at ...HttpRequestPipeReader.ReadAsync(...) (request body read aborted)
```
Both are the caller going away — `OperationCanceledException` tied to `HttpContext.RequestAborted`, and the Kestrel "client reset the request stream" IOException.
## Root cause (code)
- `SpikerSoft.Api/Middleware/GlobalExceptionHandlerMiddleware.cs:33` — `catch (Exception ex)` handles everything the same way: `LogError` (line ~75), sets `Response.StatusCode = 500` (line ~86), and marks the current Activity/span as error (`activity.SetStatus(Error)`, `error=true`). No special-casing for cancellation/client-abort.
- `SpikerSoft.Business/Behaviors/TracingBehavior.cs:86` — `catch (Exception ex)` -> `logger.LogError(ex, "{OperationType} failed: ...")` (line 101). No cancellation special-casing.
## Proposed fix
In both catch blocks, detect client-abort and treat it as non-error:
```csharp
if (ex is OperationCanceledException or TaskCanceledException
&& context.RequestAborted.IsCancellationRequested)
{
_logger.LogInformation("Request aborted by client: {Path}", context.Request.Path);
context.Response.StatusCode = 499; // client closed request (nginx convention)
return; // do NOT mark the span as error
}
// also treat IOException "client reset the request stream" (Kestrel BadHttpRequest/connection reset) as client-abort
```
For `TracingBehavior`, log `OperationCanceledException` where the token is canceled at `Debug`/`Information` and skip the `activity.SetStatus(Error)` so canceled requests don't show as failed traces.
## Impact of fixing
- Cleaner Seq Error stream (fewer false positives during triage/alerting).
- Accurate 5xx/error-rate metrics and Jaeger span success rates.
Priority: **low** (cosmetic/observability; ~6 events/6h currently), but worth doing since we rely on the error stream to find real issues.
---
_Filed proactively by automated health check (Seq centralized error audit)._
Resolved in spikersoft-backend PR #211 (merged to master). Client aborts (OperationCanceledException tied to RequestAborted; Kestrel 'client reset the request stream' IOException) now log Information + 499 with a client.abort span tag in GlobalExceptionHandlerMiddleware, and TracingBehavior filters caller-canceled OCEs to Information + mediatr.cancelled without failing the span. The boundary is preserved and tested both ways — cancellations NOT caused by the caller still log Error/500. 6 new xUnit tests; CI auto-deploys the API, so the GetLinkCommentsQuery and check-email noise drops out of the Seq Error stream on the next rollout. Closing.
Resolved in spikersoft-backend PR #211 (merged to master). Client aborts (OperationCanceledException tied to RequestAborted; Kestrel 'client reset the request stream' IOException) now log Information + 499 with a client.abort span tag in GlobalExceptionHandlerMiddleware, and TracingBehavior filters caller-canceled OCEs to Information + mediatr.cancelled without failing the span. The boundary is preserved and tested both ways — cancellations NOT caused by the caller still log Error/500. 6 new xUnit tests; CI auto-deploys the API, so the GetLinkCommentsQuery and check-email noise drops out of the Seq Error stream on the next rollout. Closing.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Client-driven request aborts/cancellations are logged at
Errorlevel and returned as HTTP 500 (and marked as errors on the Jaeger span). These are not server faults — the client disconnected or canceled mid-request — but they pollute the Error stream in Seq (the same stream used for alerting/triage) and inflate the API's error/500 metrics. Low functional impact, real observability impact.Evidence (Seq, last 6h)
SpikerSoft.Business.Behaviors.TracingBehavior—Query failed: GetLinkCommentsQuery(4 events), exception:SpikerSoft.Api.Middleware.GlobalExceptionHandlerMiddleware—Unhandled exception ... POST /api/authentication/check-email(UserId: anonymous), exception:Both are the caller going away —
OperationCanceledExceptiontied toHttpContext.RequestAborted, and the Kestrel "client reset the request stream" IOException.Root cause (code)
SpikerSoft.Api/Middleware/GlobalExceptionHandlerMiddleware.cs:33—catch (Exception ex)handles everything the same way:LogError(line ~75), setsResponse.StatusCode = 500(line ~86), and marks the current Activity/span as error (activity.SetStatus(Error),error=true). No special-casing for cancellation/client-abort.SpikerSoft.Business/Behaviors/TracingBehavior.cs:86—catch (Exception ex)->logger.LogError(ex, "{OperationType} failed: ...")(line 101). No cancellation special-casing.Proposed fix
In both catch blocks, detect client-abort and treat it as non-error:
For
TracingBehavior, logOperationCanceledExceptionwhere the token is canceled atDebug/Informationand skip theactivity.SetStatus(Error)so canceled requests don't show as failed traces.Impact of fixing
Priority: low (cosmetic/observability; ~6 events/6h currently), but worth doing since we rely on the error stream to find real issues.
Filed proactively by automated health check (Seq centralized error audit).
Resolved in spikersoft-backend PR #211 (merged to master). Client aborts (OperationCanceledException tied to RequestAborted; Kestrel 'client reset the request stream' IOException) now log Information + 499 with a client.abort span tag in GlobalExceptionHandlerMiddleware, and TracingBehavior filters caller-canceled OCEs to Information + mediatr.cancelled without failing the span. The boundary is preserved and tested both ways — cancellations NOT caused by the caller still log Error/500. 6 new xUnit tests; CI auto-deploys the API, so the GetLinkCommentsQuery and check-email noise drops out of the Seq Error stream on the next rollout. Closing.