Skip to content

fix(proxy): route on pathname so query strings match - #86

Merged
robertn702 merged 2 commits into
mainfrom
robertn702/fix-v1-models-query-3
Oct 24, 2026
Merged

robertn702 merged 2 commits into
mainfrom
robertn702/fix-v1-models-query-3

Conversation

@robertn702

@robertn702 robertn702 commented Oct 24, 2026 •

Copy link
Copy Markdown
Owner

What this fixes

GET /v1/models?client_version=1 returned 404 because the proxy matched routes against request.url, which includes the query string (#84). Codex appends ?client_version=<version> when refreshing its model catalog, so the refresh failed on every session. /health, /ready, /v1/responses, and /v1/messages had the same exact-match problem.

What changed

  • packages/proxy/src/server.ts
    • New requestPath(request): returns request.url with everything from the first ? removed.
    • createAppServer request handler: the draining check (/health, /ready) and the in-flight proxied check use requestPath.
    • handle: every route match (/health, /ready, /v1/models, /v1/responses, /v1/messages) and the provider selection use requestPath. Method checks and the 404 body for unknown paths are unchanged.
    • I chose stripping at ? over new URL(request.url, "http://localhost").pathname: it can't throw on a malformed request target and doesn't reinterpret //host/... targets, so absolute-form targets (covered by the existing Host validation test) still return 404.
  • packages/proxy/test/server.test.ts: new "query strings" test. With a query string appended, each route still reaches its handler (/health, /ready, /v1/models, /v1/responses, /v1/messages), and /v1/unknown?client_version=1 still returns 404 {"error":"not_found","message":"unknown route"}. Before the fix this test fails with expected 404 to be 200.
  • .changeset/proxy-route-query-strings.md: patch bump for @reasoning-router/proxy.

The query string still isn't forwarded upstream; /v1/models keeps calling <upstream>/models, which is how it already behaved for bare requests.

Verification

npm run check passed with exit 0: typecheck passed, Biome reported no errors, and Vitest ran 36 files with 477 tests passing. The one Biome warning is already on main, in packages/classifiers/test/jev.test.ts.

Built the proxy (npm run build) and ran it on port 4392 against a stub upstream on 4391 (REASONING_ROUTER_UPSTREAM_AUTH=forward).

Before, with server.ts from main:

$ curl -s -w '\n%{http_code}\n' 'http://127.0.0.1:4392/v1/models'
{"object":"list","data":[{"id":"gpt-6-astra","object":"model"}],"path":"/v1/models"}
200
$ curl -s -w '\n%{http_code}\n' 'http://127.0.0.1:4392/v1/models?client_version=1'
{"error":"not_found","message":"unknown route"}
404

After:

$ curl -s -w '\n%{http_code}\n' 'http://127.0.0.1:4392/v1/models'
{"object":"list","data":[{"id":"gpt-6-astra","object":"model"}],"path":"/v1/models"}
200
$ curl -s -w '\n%{http_code}\n' 'http://127.0.0.1:4392/v1/models?client_version=1'
{"object":"list","data":[{"id":"gpt-6-astra","object":"model"}],"path":"/v1/models"}
200

Fixes #84

Summary by CodeRabbit

  • Bug Fixes
    • Requests to recognized health, readiness, model-catalog, and provider endpoints now work when the URL includes a query string.
    • Unknown paths with query strings continue to return a 404 response.

request.url includes the query string, so exact matches like
/v1/models missed any request with query parameters and returned 404.
Codex appends ?client_version=<version> when refreshing its model
catalog, so every refresh failed. Match /health, /ready, /v1/models,
/v1/responses, and /v1/messages on the path with the query stripped.

Strip at the first '?' rather than parsing with new URL(): it cannot
throw on a malformed target and does not reinterpret '//host/...'
targets, so absolute-form and other unknown targets still 404 as before.

Fixes #84
@coderabbitai

coderabbitai Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 51 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 3062c4e5-a35b-4364-ae65-b2532d7748af
Commits

Reviewing files that changed from the base of the PR and between 2849392 and 77621f8.

Files selected for processing: 1
  • packages/proxy/test/server.test.ts

No actionable comments were generated in the recent review.

Recent review info
Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: defaults
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: e574caac-8b35-4cb5-be4d-ae4c63417267
Commits

Reviewing files that changed from the base of the PR and between db653e0 and 2849392.

Files selected for processing: 3
  • .changeset/proxy-route-query-strings.md
  • packages/proxy/src/server.ts
  • packages/proxy/test/server.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Walkthrough

The proxy removes query strings before matching routes for health, readiness, models, Responses, and configured Anthropic Messages requests. Tests cover queried routes and confirm that unknown paths still return 404.

Changes: Proxy route matching

Layer / File(s) Summary
Query-free route matching and validation
packages/proxy/src/server.ts, packages/proxy/test/server.test.ts, .changeset/proxy-route-query-strings.md
The proxy uses the query-free path for draining checks and route matching. Integration tests cover queried health, readiness, model, Responses, Messages, and unknown routes. The changeset records the patch.

Priority: Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix · Severity of issue fixed: Medium

Possibly related PRs

Merge Risk: Minimal · up to 28493

Queried routes can now match without changing upstream requests. No merge-blocking issue was identified.

Pre-merge checks: Passed 4 · Failed 1

Failed Failed checks: 1 warning

Check name Status Explanation Resolution
Docstring Coverage Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 2 files. (1 skipped: 1 … Write docstrings for the functions missing them to satisfy the coverage threshold.
Passed Passed checks: 4 passed
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the main change: proxy routes now match on the pathname so query strings do not prevent recognized routes from matching.
Linked Issues check Passed Issue #84 requires query-bearing requests to match the route path. requestPath removes the query from request.url, and server.ts uses it for health, readiness, model, Responses, Messages, draini…
Out of Scope Changes check Passed The changes are limited to proxy route matching, focused query-string coverage, and a patch changeset for @reasoning-router/proxy. The test changes directly verify issue #84 and preserve unknown-rou…
Full details: Linked Issues check

Explanation

Issue #84 requires query-bearing requests to match the route path. requestPath removes the query from request.url, and server.ts uses it for health, readiness, model, Responses, Messages, draining, in-flight, and provider checks. The added test covers all five routes with queries and verifies that /v1/unknown?client_version=1 keeps the existing 404 response. The implementation also keeps upstream model forwarding at /models, as required by the current behavior.

Full details: Out of Scope Changes check

Explanation

The changes are limited to proxy route matching, focused query-string coverage, and a patch changeset for @reasoning-router/proxy. The test changes directly verify issue #84 and preserve unknown-route behavior. No unrelated implementation or test changes are shown in the supplied summary and file evidence.

Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 2 files. (1 skipped: 1 unsupported.)

Resolution

Write docstrings for the functions missing them to satisfy the coverage threshold.

Finishing Touches: 💡 1
📝 Generate docstrings: 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

/v1/messages only routes when an Anthropic upstream is configured. The
query-string fix touches that predicate, so assert the 404 still holds for
/v1/messages?beta=true on an app built without an Anthropic upstream.
@robertn702
robertn702 merged commit 866a2ca into main Oct 24, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GET /v1/models returns 404 when the client appends a query string

1 participant