Skip to content

fix: request aborted shoudn't result in a fetch error#2741

Merged
SkArchon merged 8 commits intomainfrom
milinda/eng-8828-router-request-aborted-shoudnt-result-in-a-fetch-error
Apr 22, 2026
Merged

fix: request aborted shoudn't result in a fetch error#2741
SkArchon merged 8 commits intomainfrom
milinda/eng-8828-router-request-aborted-shoudnt-result-in-a-fetch-error

Conversation

@SkArchon
Copy link
Copy Markdown
Contributor

@SkArchon SkArchon commented Apr 6, 2026

This PR fixes places where we mark spans as errors for client disconnects incorrectly.

Summary by CodeRabbit

  • Bug Fixes

    • Client disconnects/timeouts are no longer treated as server errors: traces/spans, logs, and metrics preserve non-error status and avoid emitting 500 logs; canceled requests now follow a dedicated "request canceled" response path (HTTP 200).
    • Transport and batching paths preserve non-error span status for client disconnects.
  • Refactor

    • Consolidated fetch error handling to unify metrics and span attribute reporting while preserving cancellation-specific behavior.
  • Tests

    • Added extensive telemetry and metrics tests, including stability polling for exported spans and scenarios for disconnect, persisted-operation, and batched requests.

Checklist

Open Source AI Manifesto

This project follows the principles of the Open Source AI Manifesto. Please ensure your contribution aligns with its principles.

@github-actions github-actions Bot added the router label Apr 6, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Treat client disconnects (context.Canceled) as non-fatal: record the cancellation on spans and emit exception events but avoid setting OTEL span status to ERROR, skip request-error metrics, and convert canceled operations into a REQUEST_CANCELED GraphQL response path. Tests and transport/tracing updated accordingly.

Changes

Cohort / File(s) Summary
Core error handling & batching
router/core/errors.go, router/core/batch.go
writeOperationError maps context.Canceled to a REQUEST_CANCELED GraphQL response (HTTP 200). processBatchError records cancellation on the active span via span.RecordError instead of attaching a sanitized ERROR status.
Engine loader: fetch error handling & metrics
router/core/engine_loader_hooks.go, router/core/engine_loader_hooks_test.go
Adds engineLoaderHooks.recordFetchError(...). Cancellation records the error on the span but does not set span status to ERROR or call MeasureRequestError; non-cancel errors set ERROR, emit exception and downstream events; downstream extension codes are deduplicated/sorted and appended to metric attributes. Tests cover cancellation vs real errors and downstream error event/attribute behavior.
Prehandler / operation flow
router/core/graphql_prehandler.go
When operation or persisted-operation errors are context.Canceled, record the error on router/fetch spans (RecordError) but skip marking span status ERROR and skip conditional cost-estimated header logic.
Metrics test spy
router/core/operation_metrics_test.go
spyMetricStore now captures the attribute slice passed into MeasureRequestError for assertions.
Transport tracing & tests
router/pkg/trace/transport.go, router/pkg/trace/transport_test.go
transport.RoundTrip treats context.Canceled specially by setting the current span status to codes.Ok with message "client disconnected" to prevent otelhttp from marking it as error; test added asserting canceled requests export codes.Ok.
Integration telemetry tests & testenv
router-tests/telemetry/span_error_status_test.go, router-tests/telemetry/telemetry_test.go, router-tests/testenv/testenv.go
Adds waitForStableSpans helper and three subtests verifying client disconnect/timeout scenarios do not produce ERROR span statuses, include expected exception events, and emit no HTTP 500 server logs; tightens CDN auth header validation to require Bearer prefix. Also adjusts expected span attribute counts.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 30.77% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: preventing client disconnects (request aborts) from being treated as fetch errors in span status marking.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 6, 2026

Router-nonroot image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-8798f18fd570e8ac427da399c98250a4a5163eba-nonroot

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 6, 2026

Codecov Report

❌ Patch coverage is 92.13483% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 65.99%. Comparing base (1ac6450) to head (4ce8468).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
router/core/batch.go 33.33% 2 Missing and 2 partials ⚠️
router/core/engine_loader_hooks.go 96.22% 1 Missing and 1 partial ⚠️
router/core/graphql_prehandler.go 93.75% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2741      +/-   ##
==========================================
+ Coverage   65.62%   65.99%   +0.37%     
==========================================
  Files         254      254              
  Lines       26401    33221    +6820     
==========================================
+ Hits        17325    21924    +4599     
- Misses       7670     9908    +2238     
+ Partials     1406     1389      -17     
Files with missing lines Coverage Δ
router/core/errors.go 80.56% <100.00%> (+0.85%) ⬆️
router/pkg/trace/transport.go 100.00% <100.00%> (ø)
router/core/graphql_prehandler.go 85.34% <93.75%> (+0.65%) ⬆️
router/core/engine_loader_hooks.go 90.52% <96.22%> (-1.46%) ⬇️
router/core/batch.go 79.65% <33.33%> (-3.28%) ⬇️

... and 235 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
router/core/batch.go (1)

183-212: ⚠️ Potential issue | 🟠 Major

Return immediately for canceled batch requests.

This branch still falls through to writeRequestErrors, so a batched disconnect will try to serialize a GraphQL error after we've already classified the failure as a client-side cancellation. That can reintroduce write-side noise and mutate the observed status in the batch path.

💡 Proposed fix
func processBatchError(w http.ResponseWriter, r *http.Request, err error, requestLogger *zap.Logger) {
-	if errors.Is(err, context.Canceled) {
-		span := trace.SpanFromContext(r.Context())
-		span.RecordError(err)
-	} else {
-		ctrace.AttachErrToSpanFromContext(r.Context(), err)
-	}
+	if errors.Is(err, context.Canceled) {
+		trace.SpanFromContext(r.Context()).RecordError(err)
+		return
+	}
+
+	ctrace.AttachErrToSpanFromContext(r.Context(), err)

	requestError := graphqlerrors.RequestError{
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router/core/batch.go` around lines 183 - 212, In processBatchError, the
context.Canceled branch records the span but then falls through to
writeRequestErrors; modify processBatchError so that when errors.Is(err,
context.Canceled) is true you RecordError on the span (as now) and then
immediately return to avoid calling writeRequestErrors and serializing a GraphQL
error for client cancellations; keep the existing else branch
(ctrace.AttachErrToSpanFromContext) and the subsequent handling for non-canceled
errors intact.
🧹 Nitpick comments (2)
router/core/engine_loader_hooks_test.go (2)

373-392: Assert the merged attrs at the metric-store boundary too.

Right now this only checks the returned slice. The test would still pass if recordFetchError returned the merged attrs but called MeasureRequestError with the old slice.

Suggested assertion
 		resultSlice, _ := hooks.recordFetchError(ctx, span, fetchErr, rc, nil, metricAddOpt, prePopulated)
 		span.End()
@@
 		require.True(t, hasExisting, "pre-populated attrs should be preserved")
 		require.True(t, hasErrorCodes, "error codes should be appended")
+
+		require.True(t, store.requestErrorCalled, "MeasureRequestError should be called")
+
+		var metricHasExisting, metricHasErrorCodes bool
+		for _, attr := range store.requestErrorSliceAttr {
+			if string(attr.Key) == "existing.attr" {
+				metricHasExisting = true
+			}
+			if string(attr.Key) == "graphql.error.codes" {
+				metricHasErrorCodes = true
+			}
+		}
+		require.True(t, metricHasExisting,
+			"MeasureRequestError should receive the pre-populated attrs")
+		require.True(t, metricHasErrorCodes,
+			"MeasureRequestError should receive the appended error codes")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router/core/engine_loader_hooks_test.go` around lines 373 - 392, The test
currently only asserts the returned slice from hooks.recordFetchError contains
merged attributes; extend it to also verify that the metric-store call received
the merged slice: capture the arguments passed to the mock
MetricStore.MeasureRequestError (or the equivalent MeasureRequestError spy used
by hooks), and assert that the captured attrs include both the prePopulated
attribute ("existing.attr") and the "graphql.error.codes" attribute (same checks
used for resultSlice). Update references to the mock/spy used by the hooks setup
(e.g., the MetricStore mock instance) and reuse
metricAddOpt/prePopulated/resultSlice names to ensure MeasureRequestError was
invoked with the merged attrs.

107-130: Assert the exception event for wrapped cancellations too.

This case currently only proves the status/metric behavior. If the wrapped context.Canceled path stops emitting the observability event, the regression would still pass.

Suggested assertion
 		require.NotEqual(t, codes.Error, spans[0].Status().Code,
 			"wrapped context.Canceled should not set span status to Error")
+		require.Len(t, spans[0].Events(), 1,
+			"wrapped context.Canceled should still be recorded as a span event")
+		require.Equal(t, "exception", spans[0].Events()[0].Name)
 		require.False(t, store.requestErrorCalled,
 			"MeasureRequestError should not be called for wrapped context.Canceled")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router/core/engine_loader_hooks_test.go` around lines 107 - 130, The test
"wrapped context.Canceled does not set span ERROR status" currently omits
asserting that an observability exception event is still emitted for wrapped
cancellations; update the test after calling hooks.OnFinished(ctx, ds,
&resolve.ResponseInfo{Err: wrappedErr}) to inspect the recorded span (spans[0])
events and assert that there is an "exception" event (or an event whose
attributes indicate an exception/exception.type/exception.message matching
wrappedErr) so the wrapped context.Canceled path still emits the expected
exception event; use the existing exporter.GetSpans().Snapshots() and
spans[0].Events() APIs to locate and assert the event.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@router-tests/telemetry/span_error_status_test.go`:
- Around line 188-191: Replace the fixed time.Sleep usage with
require.Eventually to avoid races when waiting for async span/log export:
instead of sleeping then calling exporter.GetSpans().Snapshots() and
require.NotEmpty(t, spans), call require.Eventually with a short polling
interval and timeout and inside the poll invoke exporter.GetSpans().Snapshots()
and assert len(spans) > 0 (or use require.NotEmpty within the closure) so the
test waits until all expected spans are exported; update the same pattern found
around the other occurrences that use time.Sleep before checking
exporter.GetSpans().Snapshots() (the instances at the other mentioned blocks
should be changed similarly).

In `@router/core/engine_loader_hooks.go`:
- Around line 274-331: The recordFetchError helper currently records the error
and metrics but never sets the span attribute that marks non-canceled request
failures; update recordFetchError to set the span attribute
rotel.WgRequestError.Bool(true) for real fetch failures (i.e., when fetchErr is
not context.Canceled and not context.DeadlineExceeded or equivalent cancellation
checks) before returning so traces and metrics stay in sync; reference the
recordFetchError function and rotel.WgRequestError to locate where to add
span.SetAttributes(...) alongside the existing metricAttrs append and
otelmetric.WithAttributeSet creation.

---

Outside diff comments:
In `@router/core/batch.go`:
- Around line 183-212: In processBatchError, the context.Canceled branch records
the span but then falls through to writeRequestErrors; modify processBatchError
so that when errors.Is(err, context.Canceled) is true you RecordError on the
span (as now) and then immediately return to avoid calling writeRequestErrors
and serializing a GraphQL error for client cancellations; keep the existing else
branch (ctrace.AttachErrToSpanFromContext) and the subsequent handling for
non-canceled errors intact.

---

Nitpick comments:
In `@router/core/engine_loader_hooks_test.go`:
- Around line 373-392: The test currently only asserts the returned slice from
hooks.recordFetchError contains merged attributes; extend it to also verify that
the metric-store call received the merged slice: capture the arguments passed to
the mock MetricStore.MeasureRequestError (or the equivalent MeasureRequestError
spy used by hooks), and assert that the captured attrs include both the
prePopulated attribute ("existing.attr") and the "graphql.error.codes" attribute
(same checks used for resultSlice). Update references to the mock/spy used by
the hooks setup (e.g., the MetricStore mock instance) and reuse
metricAddOpt/prePopulated/resultSlice names to ensure MeasureRequestError was
invoked with the merged attrs.
- Around line 107-130: The test "wrapped context.Canceled does not set span
ERROR status" currently omits asserting that an observability exception event is
still emitted for wrapped cancellations; update the test after calling
hooks.OnFinished(ctx, ds, &resolve.ResponseInfo{Err: wrappedErr}) to inspect the
recorded span (spans[0]) events and assert that there is an "exception" event
(or an event whose attributes indicate an
exception/exception.type/exception.message matching wrappedErr) so the wrapped
context.Canceled path still emits the expected exception event; use the existing
exporter.GetSpans().Snapshots() and spans[0].Events() APIs to locate and assert
the event.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 95ba86f9-0380-4da8-8e23-5351e8b8d117

📥 Commits

Reviewing files that changed from the base of the PR and between 2326f9c and 1e7948c.

📒 Files selected for processing (9)
  • router-tests/telemetry/span_error_status_test.go
  • router/core/batch.go
  • router/core/engine_loader_hooks.go
  • router/core/engine_loader_hooks_test.go
  • router/core/errors.go
  • router/core/graphql_prehandler.go
  • router/core/operation_metrics_test.go
  • router/pkg/trace/transport.go
  • router/pkg/trace/transport_test.go

Comment thread router-tests/telemetry/span_error_status_test.go Outdated
Comment thread router/core/engine_loader_hooks.go
@SkArchon SkArchon force-pushed the milinda/eng-8828-router-request-aborted-shoudnt-result-in-a-fetch-error branch from 855b64e to 1371973 Compare April 20, 2026 13:09
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
router-tests/telemetry/span_error_status_test.go (1)

190-199: ⚠️ Potential issue | 🟡 Minor

require.Eventually still waits on partial export conditions.

These waits can pass before all relevant spans are exported, which can make the later “no span has codes.Error” assertions flaky or incomplete under slower export cycles. Please wait for a complete/settled snapshot before asserting across all spans.

As per coding guidelines, "For periodic exporters, wait for ALL expected items using require.Eventually, not just one sentinel value, to avoid race conditions with export cycles".

Also applies to: 294-303, 363-366

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router-tests/telemetry/span_error_status_test.go` around lines 190 - 199, The
test uses require.Eventually with a sentinel that returns true once a single
"Engine - Fetch" span appears, which can pass before all spans are exported and
causes flakiness; update the require.Eventually predicate that calls
exporter.GetSpans().Snapshots() so it waits for a complete/settled snapshot
(e.g., check that the total number of exported spans equals the expected count
or that no more new snapshots appear for a short interval) before returning
true, and then perform the "no span has codes.Error" assertions against that
settled snapshot; apply the same change to the other occurrences using
require.Eventually + exporter.GetSpans().Snapshots() in this file (the blocks
looking for "Engine - Fetch" and later assertions).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@router-tests/telemetry/span_error_status_test.go`:
- Around line 248-257: The handler currently slices the Authorization header
with authorization[len("Bearer "):], which can panic for short or non-"Bearer "
values; replace that unsafe slice in both occurrences with a safe prefix check
using strings.CutPrefix (or strings.HasPrefix + slicing after verifying length):
check that authorization is non-empty and that strings.CutPrefix(authorization,
"Bearer ") returns token and true, otherwise respond http.StatusUnauthorized;
then pass the safe token into jwtParser.ParseUnverified (the
jwtParser.ParseUnverified call and parsedClaims usage remain unchanged). Also
add an import for strings if missing.

---

Duplicate comments:
In `@router-tests/telemetry/span_error_status_test.go`:
- Around line 190-199: The test uses require.Eventually with a sentinel that
returns true once a single "Engine - Fetch" span appears, which can pass before
all spans are exported and causes flakiness; update the require.Eventually
predicate that calls exporter.GetSpans().Snapshots() so it waits for a
complete/settled snapshot (e.g., check that the total number of exported spans
equals the expected count or that no more new snapshots appear for a short
interval) before returning true, and then perform the "no span has codes.Error"
assertions against that settled snapshot; apply the same change to the other
occurrences using require.Eventually + exporter.GetSpans().Snapshots() in this
file (the blocks looking for "Engine - Fetch" and later assertions).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8780399f-b618-403a-acca-7d43441579a6

📥 Commits

Reviewing files that changed from the base of the PR and between 1e7948c and 855b64e.

📒 Files selected for processing (3)
  • router-tests/telemetry/span_error_status_test.go
  • router/core/engine_loader_hooks.go
  • router/core/engine_loader_hooks_test.go
✅ Files skipped from review due to trivial changes (1)
  • router/core/engine_loader_hooks_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • router/core/engine_loader_hooks.go

Comment thread router-tests/telemetry/span_error_status_test.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
router-tests/telemetry/span_error_status_test.go (1)

248-257: ⚠️ Potential issue | 🟠 Major

Guard Bearer token parsing to avoid panic on malformed Authorization headers.

Line 253 slices authorization[len("Bearer "):] without validating the prefix, which can panic for short/non-Bearer values in this mock handler.

Proposed fix
 			authorization := r.Header.Get("Authorization")
-			if authorization == "" {
+			token, ok := strings.CutPrefix(authorization, "Bearer ")
+			if !ok || token == "" {
 				w.WriteHeader(http.StatusUnauthorized)
 				return
 			}
-			token := authorization[len("Bearer "):]
 			parsedClaims := make(jwt.MapClaims)
 			jwtParser := new(jwt.Parser)
 			_, _, err := jwtParser.ParseUnverified(token, parsedClaims)
#!/bin/bash
# Verify unsafe bearer slicing is removed and safe prefix parsing is used.
rg -nP --type go -C2 '\[\s*len\("Bearer "\)\s*:\s*\]' router-tests/telemetry/span_error_status_test.go router-tests/testenv/testenv.go
rg -n --type go -C2 'strings\.CutPrefix\(\s*authorization,\s*"Bearer "\s*\)' router-tests/telemetry/span_error_status_test.go router-tests/testenv/testenv.go
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router-tests/telemetry/span_error_status_test.go` around lines 248 - 257, The
handler currently slices authorization[len("Bearer "):] unsafely which can panic
on short/malformed Authorization headers; update the authorization parsing in
the test handler to safely verify the prefix (e.g., use strings.HasPrefix or
strings.CutPrefix on the authorization variable) before extracting token, return
http.StatusUnauthorized if the prefix is missing or token is empty, and then
proceed to call jwtParser.ParseUnverified with the safe token and parsedClaims
(refer to the authorization, token, jwtParser, and parsedClaims symbols to
locate the code).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@router-tests/telemetry/span_error_status_test.go`:
- Around line 190-199: The test currently returns from require.Eventually as
soon as a single sentinel span ("Engine - Fetch") appears which can miss
late-exported spans; update the polling lambda used with require.Eventually (the
one calling exporter.GetSpans().Snapshots()) to wait until the full set of
expected spans is present (e.g., verify the snapshot contains all required span
names or a minimum expected length) before returning true, and apply the same
change to the other two polling sites around lines where fetchSpan is set so
subsequent assertions operate on a complete snapshot.

---

Duplicate comments:
In `@router-tests/telemetry/span_error_status_test.go`:
- Around line 248-257: The handler currently slices authorization[len("Bearer
"):] unsafely which can panic on short/malformed Authorization headers; update
the authorization parsing in the test handler to safely verify the prefix (e.g.,
use strings.HasPrefix or strings.CutPrefix on the authorization variable) before
extracting token, return http.StatusUnauthorized if the prefix is missing or
token is empty, and then proceed to call jwtParser.ParseUnverified with the safe
token and parsedClaims (refer to the authorization, token, jwtParser, and
parsedClaims symbols to locate the code).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 172711aa-acf2-4b8c-980e-96931bb2bba6

📥 Commits

Reviewing files that changed from the base of the PR and between 855b64e and 1371973.

📒 Files selected for processing (3)
  • router-tests/telemetry/span_error_status_test.go
  • router/core/engine_loader_hooks.go
  • router/core/engine_loader_hooks_test.go
🚧 Files skipped from review as they are similar to previous changes (2)
  • router/core/engine_loader_hooks_test.go
  • router/core/engine_loader_hooks.go

Comment thread router-tests/telemetry/span_error_status_test.go Outdated
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
router-tests/telemetry/span_error_status_test.go (1)

239-245: Consider clarifying the comment to match the assertion behavior.

The comment states the attribute "should not be set" but the code only verifies it's not true when present. Both achieve the test's goal, but the comment could be more precise.

📝 Suggested comment clarification
-			// The wg.request.error attribute should not be set on the fetch span
+			// The wg.request.error attribute should not be true on the fetch span
 			for _, attr := range fetchSpan.Attributes() {
 				if attr.Key == "wg.request.error" {
 					require.False(t, attr.Value.AsBool(),
 						"wg.request.error should not be true on fetch span for client disconnects")
 				}
 			}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router-tests/telemetry/span_error_status_test.go` around lines 239 - 245,
Update the comment above the loop over fetchSpan.Attributes() to accurately
describe the test: instead of saying "The wg.request.error attribute should not
be set on the fetch span", state that the test verifies the attribute is not
true when present (e.g., "If wg.request.error is present on the fetch span, it
should not be true"); refer to the loop checking fetchSpan.Attributes() and the
assertion that attr.Value.AsBool() is false for attr.Key == "wg.request.error".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@router-tests/telemetry/span_error_status_test.go`:
- Around line 239-245: Update the comment above the loop over
fetchSpan.Attributes() to accurately describe the test: instead of saying "The
wg.request.error attribute should not be set on the fetch span", state that the
test verifies the attribute is not true when present (e.g., "If wg.request.error
is present on the fetch span, it should not be true"); refer to the loop
checking fetchSpan.Attributes() and the assertion that attr.Value.AsBool() is
false for attr.Key == "wg.request.error".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 9d2f337d-5673-4e3d-b143-def26637e727

📥 Commits

Reviewing files that changed from the base of the PR and between 1371973 and 95b9e3a.

📒 Files selected for processing (3)
  • router-tests/telemetry/span_error_status_test.go
  • router-tests/testenv/testenv.go
  • router/core/errors.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • router/core/errors.go

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
router-tests/telemetry/telemetry_test.go (1)

7740-7740: Assert wg.request.error=true explicitly (not only attribute count).

Line 7740 validates only total attribute count. Please also assert otel.WgRequestError.Bool(true) here so the test fails on semantic regressions, not just count changes.

Suggested patch
-			require.Lenf(t, sn[8].Attributes(), 15, "expected 15 attributes, got %d", len(sn[8].Attributes()))
+			require.Lenf(t, sn[8].Attributes(), 15, "expected 15 attributes, got %d", len(sn[8].Attributes()))
+			require.Contains(t, sn[8].Attributes(), otel.WgRequestError.Bool(true))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@router-tests/telemetry/telemetry_test.go` at line 7740, The test currently
only asserts the attribute count for span sn[8]; additionally assert that the
span contains the "wg.request.error" attribute set to true by checking
otel.WgRequestError.Bool(true) against sn[8].Attributes() (e.g., use
require.True or require.Equal to assert the attribute exists and is true for
sn[8]). Locate the assertion around require.Lenf(t, sn[8].Attributes(), 15, ...)
and add a line that explicitly verifies otel.WgRequestError.Bool(true) is
present/true on sn[8] to catch semantic regressions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@router-tests/telemetry/telemetry_test.go`:
- Line 7740: The test currently only asserts the attribute count for span sn[8];
additionally assert that the span contains the "wg.request.error" attribute set
to true by checking otel.WgRequestError.Bool(true) against sn[8].Attributes()
(e.g., use require.True or require.Equal to assert the attribute exists and is
true for sn[8]). Locate the assertion around require.Lenf(t, sn[8].Attributes(),
15, ...) and add a line that explicitly verifies otel.WgRequestError.Bool(true)
is present/true on sn[8] to catch semantic regressions.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 5a3fc53e-1e24-4dbb-a51e-c8500c540306

📥 Commits

Reviewing files that changed from the base of the PR and between 95b9e3a and 9269b78.

📒 Files selected for processing (1)
  • router-tests/telemetry/telemetry_test.go

@SkArchon SkArchon marked this pull request as ready for review April 21, 2026 08:33
Comment thread router-tests/telemetry/span_error_status_test.go
Comment thread router-tests/telemetry/span_error_status_test.go Outdated
Comment thread router-tests/telemetry/span_error_status_test.go
Comment thread router-tests/testenv/testenv.go Outdated
Comment thread router/core/errors.go
Comment thread router/core/graphql_prehandler.go
Comment thread router/pkg/trace/transport_test.go Outdated
Copy link
Copy Markdown
Member

@endigma endigma left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@SkArchon SkArchon merged commit b712757 into main Apr 22, 2026
52 of 53 checks passed
@SkArchon SkArchon deleted the milinda/eng-8828-router-request-aborted-shoudnt-result-in-a-fetch-error branch April 22, 2026 13:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants