Skip to content

fix: ensure router header expressions are set even on early return errors#2492

Merged
SkArchon merged 15 commits into
mainfrom
milinda/expression-header-updates
Feb 8, 2026
Merged

fix: ensure router header expressions are set even on early return errors#2492
SkArchon merged 15 commits into
mainfrom
milinda/expression-header-updates

Conversation

@SkArchon

@SkArchon SkArchon commented Feb 5, 2026

Copy link
Copy Markdown
Contributor

This PR fixes a bug where we do not process router response header expressions on early returns on errors, some of these errors are as follow

  • Invalid queries
  • Invalid PO operation
  • http errors
    etc

In addition, we skip processing the header for early returns of subscription operation types, this is as we currently do not process the router response header expressions when the request is successful for subscriptions, so we dont process them when they are not successful either.

Due to refactoring code, codecov has highlighted quite a few places which did not have code coverage before, I have added tests for those as well. However a few still remain missing as they seem to be "unreachable", and might need more investigation / be covered by unit tests.

Summary by CodeRabbit

  • New Features

    • Exposed a configurable header-propagation option in graph/router setup.
  • Bug Fixes

    • More consistent error-response header behavior; subscriptions no longer propagate custom headers; improved logging for response write/flush failures.
  • Tests

    • Added extensive tests for header propagation on error responses, persisted-query/JWT error flows, SSE/multipart/body write failures, and subscription flush errors.
  • Tests (helpers)

    • Added test scaffolding and modules to simulate failing writers, non-flusher writers, and related token/registration test scenarios.

Checklist

  • I have discussed my proposed changes in an issue and have received approval to proceed.
  • I have followed the coding standards of the project.
  • Tests or benchmarks have been added or updated.
  • Documentation has been updated on https://github.com/wundergraph/cosmo-docs.
  • I have read the Contributors Guide.

@coderabbitai

coderabbitai Bot commented Feb 5, 2026

Copy link
Copy Markdown
Contributor

Walkthrough

Threads header propagation into router error paths, refactors error-writing to use a single params struct, and adds tests plus test modules for header propagation, response-write failures, and non-flusher-writer behavior.

Changes

Cohort / File(s) Summary
Header propagation tests
router-tests/header_propagation_test.go
Adds TestHeaderPropagationOnErrorResponses, exposes mockSelfRegister for test self-registration, and adds subtests for multiple error scenarios validating propagated headers and JWT-related flows.
Error-writing refactor (core)
router/core/errors.go, router/core/graphql_handler.go, router/core/modules.go, router/core/batch.go
Introduces writeRequestErrorsParams and changes writeRequestErrors to accept the params struct; updates all call sites to pass request, writer, statusCode, requestErrors, logger, and headerPropagation.
HeaderPropagation wiring
router/core/graph_server.go, router/core/graphql_prehandler.go
Adds HeaderPropagation field to BuildGraphMuxOptions and to PreHandlerOptions/PreHandler, and wires the flag through graph mux and pre-handler initialization.
Write-failure & subscription tests
router-tests/error_handling_test.go, router-tests/http_subscriptions_test.go
Adds tests for SSE/error-body/multipart write failures with logging assertions and TestNonFlusherWriterSubscriptionError to validate subscriptions when writer lacks Flush.
Test modules: failing & non-flusher writers
router-tests/modules/failing-writer/module.go, router-tests/modules/non-flusher-writer/module.go
Adds FailingWriterModule (configurable broken-pipe/generic write failures) and NonFlusherWriterModule (wraps writer to omit Flusher) used to simulate write/flush failure scenarios in tests.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: ensuring router header expressions are set even on early return errors, which directly aligns with the core objective of the PR.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

@github-actions

github-actions Bot commented Feb 5, 2026

Copy link
Copy Markdown

Router-nonroot image scan passed

✅ No security vulnerabilities found in image:

ghcr.io/wundergraph/cosmo/router:sha-43ea61919fb156b1541dfe1c93c23b58b6d4fb62-nonroot

@codecov

codecov Bot commented Feb 5, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 67.18750% with 42 lines in your changes missing coverage. Please review.
✅ Project coverage is 61.70%. Comparing base (23668c3) to head (c41a875).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
router/core/graphql_handler.go 25.00% 24 Missing ⚠️
router/core/errors.go 74.62% 16 Missing and 1 partial ⚠️
router/core/graphql_prehandler.go 92.85% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2492      +/-   ##
==========================================
+ Coverage   61.45%   61.70%   +0.24%     
==========================================
  Files         229      229              
  Lines       23934    24022      +88     
==========================================
+ Hits        14709    14823     +114     
+ Misses       7968     7949      -19     
+ Partials     1257     1250       -7     
Files with missing lines Coverage Δ
router/core/batch.go 82.92% <100.00%> (+0.87%) ⬆️
router/core/graph_server.go 82.40% <100.00%> (+0.09%) ⬆️
router/core/modules.go 68.18% <100.00%> (+5.02%) ⬆️
router/core/graphql_prehandler.go 83.23% <92.85%> (+2.68%) ⬆️
router/core/errors.go 78.53% <74.62%> (+7.45%) ⬆️
router/core/graphql_handler.go 69.23% <25.00%> (-2.73%) ⬇️

... and 6 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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@router-tests/header_propagation_test.go`:
- Around line 1534-1768: In TestHeaderPropagationOnErrorResponses, within the
"router response headers should be propagated on bad request errors" subtest,
check err returned from xEnv.MakeGraphQLRequest before accessing res (swap the
require.NoError(t, err) to immediately follow the call to
xEnv.MakeGraphQLRequest and move any uses of res or
res.Response.StatusCode/Headers after that check) so you don't dereference res
when the request failed; adjust assertions using res (require.Equal on
StatusCode and header checks) to occur after require.NoError.

In `@router/core/batch.go`:
- Around line 200-206: The batch-level error path omits header propagation:
update the call to writeRequestErrors to include headerPropagation in the
writeRequestErrorsParams (pass the handler's headerPropagation value into the
params, e.g., add headerPropagation: headerPropagation) so router-level response
header expressions are applied for batch parsing/validation failures; then
thread HeaderPropagation through the batch handler's constructor/HandlerOpts at
the call site that builds HandlerOpts (ensure the graph mux/wiring that
constructs HandlerOpts supplies the HeaderPropagation instance).

Comment thread router-tests/header_propagation_test.go
Comment thread router/core/batch.go
@SkArchon SkArchon marked this pull request as draft February 5, 2026 19:44
@SkArchon

SkArchon commented Feb 5, 2026

Copy link
Copy Markdown
Contributor Author

Moved to draft, as working on codecov changes

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@router/core/errors.go`:
- Around line 215-220: The call to params.logger in the error branch of
params.headerPropagation.ApplyRouterResponseHeaderRules can panic if
params.logger is nil; modify the block that calls ApplyRouterResponseHeaderRules
to check that params.logger != nil before calling params.logger.Error (or use a
safe fallback/no-op logger) so that logging the failure is guarded; ensure the
same nil-check pattern used in other branches of the surrounding function is
applied here for consistency with params.logger usage.

Comment thread router/core/errors.go
@SkArchon SkArchon marked this pull request as ready for review February 5, 2026 20:01

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@router-tests/error_handling_test.go`:
- Around line 1715-1723: The test is ignoring the error returned by io.ReadAll
which can hide failures; update the test around the io.ReadAll call so you
capture its error and assert it was nil (e.g., replace "body, _ :=
io.ReadAll(resp.Body)" with code that calls io.ReadAll(resp.Body), stores the
error in a variable, and calls require.NoError(t, err) before asserting
require.Empty(t, body)); reference the io.ReadAll call and the
require.NoError/require.Empty assertions to locate the change.

Comment thread router-tests/error_handling_test.go
Comment thread router/core/errors.go

@StarpTech StarpTech left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@router-tests/http_subscriptions_test.go`:
- Around line 295-307: The test creates an http.Client with no timeout (client
:= http.Client{}) which can hang; update the client instantiation used in this
test to include a timeout consistent with other tests (e.g.,
http.Client{Timeout: time.Second * 100}) so requests and io.ReadAll(resp.Body)
cannot block indefinitely; ensure you import time if not already and keep the
rest of the test flow (req, resp, defer resp.Body.Close()) unchanged.

Comment thread router-tests/http_subscriptions_test.go
@SkArchon SkArchon merged commit 53a01d1 into main Feb 8, 2026
31 checks passed
@SkArchon SkArchon deleted the milinda/expression-header-updates branch February 8, 2026 19:10
maxbol pushed a commit to maxbol/cosmo that referenced this pull request Feb 12, 2026
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