Skip to content

Router returns HTTP 200 OK for subgraph 401 error despite "pass-through" mode #2299

@yokazawa

Description

@yokazawa

Component(s)

router

Component version

v0.258.4

wgc version

latest

controlplane version

latest

router version

v0.258.4

What happened?

Description

The Cosmo Router (version 0.258.4) appears to be ignoring the subgraph_error_propagation.mode: "pass-through" setting.

When a subgraph returns an HTTP 401 status code along with a well-formed GraphQL error body ( {"errors":...} ), the router does not propagate the 401 status code. Instead, it wraps the error, adds serviceName and statusCode to the extensions field, and returns an HTTP 200 OK response to the client.

This behavior is identical to the default wrapped mode, even when pass-through is explicitly configured.

Things we have verified:

  • The query targets only a single subgraph.
  • The router config file ( router-config.yaml ) is correctly loaded (verified by setting mode to an invalid value, which caused a validation error on startup).
  • The issue persists even when bypassing Nginx and sending requests directly to the router process.
  • Setting dev_mode: false does not solve the issue.

Steps to Reproduce

  1. Configure the router ( v0.258.4 ) with the following setting in router-config.yaml:
    • subgraph_error_propagation:
        mode: "pass-through"
  2. Set up a subgraph ("backend1") that returns an HTTP 401 Unauthorized status code and a valid GraphQL error body for a specific query:
    • Status: HTTP/1.1 401 Unauthorized
    • Body: {"errors":[{"extensions":{"code":"INVALID_TOKEN"},"message":"invalid token"}]}
  3. Restart the router to apply the configuration.
  4. Send a GraphQL query to the router that targets only the "backend1" subgraph (the one returning the 401 error).
    • curl -v http://<router-host>:<router-port>/graphql ...

Expected Result

The router should respect the pass-through setting. It should propagate the subgraph's response directly to the client:

  • Status: HTTP/1.1 401 Unauthorized
  • Body: {"errors":[{"extensions":{"code":"INVALID_TOKEN"},"message":"invalid token"}]}

Actual Result

The router ignores the pass-through setting and behaves as if it's in wrapped mode. It returns an HTTP 200 OK status and wraps the error:

  • Status: HTTP/1.1 200 OK
  • Body: {"errors":[{"extensions":{"code":"INVALID_TOKEN","serviceName":"backend1","statusCode":401},"message":"invalid token"}],"data":null}

Environment information

Environment

$ docker version        
Client: Docker Engine - Community
 Version:           28.5.1
 API version:       1.47 (downgraded from 1.51)
 Go version:        go1.25.2
 Git commit:        e180ab8ab8
 Built:             Wed Oct  8 02:50:32 2025
 OS/Arch:           darwin/arm64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          27.4.0
  API version:      1.47 (minimum version 1.24)
  Go version:       go1.22.10
  Git commit:       92a8393
  Built:            Sat Dec  7 10:39:01 2024
  OS/Arch:          linux/arm64
  Experimental:     false
 containerd:
  Version:          1.7.24
  GitCommit:        88bf19b2105c8b17560993bee28a01ddc2f97182
 runc:
  Version:          1.2.2
  GitCommit:        v1.2.2-0-g7cb3632
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

Router configuration

version: "1"

graph:
  token: "${GRAPH_API_TOKEN}"

health_check_path: "/health"
graphql_path: "/api/v1/graphql"

dev_mode: true

execution_config:
  file:
    path: "/config/execution-config.local.json"
    watch: true
    watch_interval: "60s"

subgraph_error_propagation:
  mode: "pass-through"

Router execution config

Log output

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions