Skip to content

fix(inference): route Codex OAuth chat to the subscription backend (#5353) - #5371

Merged
M3gA-Mind merged 1 commit into
tinyhumansai:mainfrom
YellowSnnowmann:fix/codex-oauth-routing-5353
Aug 5, 2026
Merged

M3gA-Mind merged 1 commit into
tinyhumansai:mainfrom
YellowSnnowmann:fix/codex-oauth-routing-5353

Conversation

@YellowSnnowmann

@YellowSnnowmann YellowSnnowmann commented Aug 4, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Fixes imported Codex CLI / OpenAI OAuth chat routing through the regular OpenAI API (api.openai.com, gpt-5.5) and failing with HTTP 400 "Stream must be set to true".
  • Routes to the Codex subscription backend based on the stored credential's kind (rotation-immune), not a fragile token string-compare.

Problem

resolve_openai_codex_routing decided Codex-vs-standard by string-comparing two independently resolved OAuth tokens: bearer_key (from lookup_key_for_slug) and credentials.access_token (a second lookup_openai_oauth_credentials read). Each read can refresh the token, and OpenAI rotates the access token on every refresh, so near expiry the two reads diverged, the equality check failed, and routing silently fell back to standard OpenAI — which rejects a non-streaming gpt-5.x request with that 400.

Compounding subtlety: OAuth and API-key credentials share the same provider:openai profile store and differ only by AuthProfileKind, so a bearer string can't reveal its source.

Solution

Decide on which credential is stored, not on token equality. A new openai_bearer_is_oauth(config) helper mirrors lookup_key_for_slug's precedence (provider:openai, then bare openai) and reports the kind of the winning profile — immune to token rotation. resolve_openai_codex_routing takes that as bearer_is_oauth and routes to the Codex backend when it is true and the OAuth metadata resolved (for the account id); an API-key profile keeps the standard path. Both call sites (chat resolve_cloud_slug, list-models) pass it. openai_codex is widened to pub(crate) so the routing can be driven directly in a test. Once routing is correct the request uses the Responses API, so the streaming 400 no longer arises.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) — with only OAuth stored, routing reaches the Codex backend even when the bearer passed in differs from the stored access token (the rotation the old check tripped on); adding an active API key restores the standard path.
  • Diff coverage ≥ 80% — the added regression test exercises the changed routing lines; CI diff-cover (cargo-llvm-cov) enforces the gate.
  • Coverage matrix updated — N/A: bug fix, no added/removed/renamed feature row.
  • All affected feature IDs from the matrix are listed under ## Related — N/A: bug fix.
  • No new external network dependencies introduced — the test drives routing offline; no live OAuth/refresh.
  • Manual smoke checklist updated — N/A: fixes existing Codex-import routing; no new release-cut surface.
  • Linked issue closed via Closes #NNN.

Impact

  • Rust core; all platforms. Codex-subscription users' chat now reaches the Codex backend instead of standard OpenAI. cargo fmt + cargo clippy -p openhuman -- -D warnings clean; unit tests green.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/codex-oauth-routing-5353

…inyhumansai#5353)

After importing Codex CLI / OpenAI OAuth, chat still routed through the
regular OpenAI API (api.openai.com, gpt-5.5) and failed with HTTP 400
"Stream must be set to true". resolve_openai_codex_routing decided
Codex-vs-standard by string-comparing two independently resolved OAuth
tokens: bearer_key (from lookup_key_for_slug) and
credentials.access_token (a second lookup_openai_oauth_credentials read).
Each read can refresh the token, and OpenAI rotates the access token on
every refresh, so near expiry the two reads diverged, the equality check
failed, and routing silently fell back to standard OpenAI — which rejects
a non-streaming gpt-5.x request with that 400.

Decide on which credential is stored, not on token equality. OAuth and
API-key credentials share the same provider:openai profile store and
differ only by AuthProfileKind, so a new openai_bearer_is_oauth() helper
mirrors lookup_key_for_slug's precedence (provider:openai, then bare
openai) and reports the *kind* of the winning profile — rotation-immune.
resolve_openai_codex_routing takes that as bearer_is_oauth and routes to
the Codex backend when it is true and the OAuth metadata resolved (for
the account id); an API-key profile keeps the standard path. Both call
sites (resolve_cloud_slug, list-models) pass it. openai_codex is widened
to pub(crate) so the flow test can drive routing directly.

Regression test: with only OAuth stored, routing reaches the Codex
backend even when the bearer passed in differs from the stored access
token (the rotation the old check tripped on); adding an active API key
restores the standard path. Once routing is correct the request uses the
Responses API, so the streaming 400 no longer arises.
@YellowSnnowmann
YellowSnnowmann requested a review from a team August 4, 2026 11:51
@coderabbitai

coderabbitai Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d9ab6136-62fd-45f5-8817-69ea264800f3

📥 Commits

Reviewing files that changed from the base of the PR and between 5efb24a and 2cc476c.

📒 Files selected for processing (5)
  • src/openhuman/inference/openai_oauth/flow_tests.rs
  • src/openhuman/inference/provider/factory.rs
  • src/openhuman/inference/provider/mod.rs
  • src/openhuman/inference/provider/openai_codex.rs
  • src/openhuman/inference/provider/ops/models.rs

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

@greptile-apps

greptile-apps Bot commented Aug 4, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes Codex OAuth chat routing being silently sent to api.openai.com instead of the Codex subscription backend, causing HTTP 400 "Stream must be set to true" errors. The root cause was that routing compared two independently-resolved OAuth access tokens; because OpenAI rotates the token on every refresh, the two reads could diverge near expiry and the equality check fell back to standard OpenAI.

  • resolve_openai_codex_routing now accepts a precomputed bearer_is_oauth: bool flag instead of doing a second credential read; routing reaches the Codex backend whenever the stored credential is OAuth and the OAuth metadata lookup succeeds (bearer_is_oauth && credentials.is_some()).
  • openai_bearer_is_oauth mirrors lookup_key_for_slug's profile-precedence logic (provider:openai → openai) and returns the stored profile's kind, making the decision rotation-immune. Both call sites (resolve_cloud_slug and list_configured_models_from_config) precompute and pass the flag.
  • A regression test verifies that OAuth-only routing reaches the Codex backend even when the bearer string passed in is deliberately stale (the rotation scenario), and that adding an API-key profile restores the standard path.

Confidence Score: 4/5

Safe to merge; the fix is conceptually correct and the regression test directly exercises the broken scenario.

The routing logic is sound and the new test covers the rotation edge-case well. Two minor concerns in openai_bearer_is_oauth are worth tracking: when no auth profile is stored, lookup_openai_oauth_credentials is called twice (once in the fallback, once inside resolve_openai_codex_routing), and the credential-emptiness check uses .trim().is_empty() while lookup_key_for_slug uses bare .is_empty() — a whitespace-only token would cause the two functions to disagree about which profile wins. Neither issue affects the happy path or breaks existing behavior.

Files Needing Attention: src/openhuman/inference/provider/factory.rs — the openai_bearer_is_oauth fallback branch and credential-emptiness gate warrant a second look.

Important Files Changed

Filename Overview
src/openhuman/inference/provider/factory.rs Adds openai_bearer_is_oauth helper and wires it into resolve_cloud_slug; the helper's fallback path has a redundant double lookup_openai_oauth_credentials call and its credential-emptiness check diverges slightly from lookup_key_for_slug's is_empty() gate.
src/openhuman/inference/provider/openai_codex.rs Adds bearer_is_oauth: bool parameter to resolve_openai_codex_routing and replaces the token-equality check with bearer_is_oauth && credentials.is_some(); logic is correct and the fix is well-targeted.
src/openhuman/inference/openai_oauth/flow_tests.rs Adds a regression test covering the rotation scenario (stale bearer string still routes to Codex) and the API-key-wins case; test structure and assertions are correct.
src/openhuman/inference/provider/mod.rs Widens openai_codex module visibility from private to pub(crate) so the new test in flow_tests.rs can reach resolve_openai_codex_routing; minimal and appropriate change.
src/openhuman/inference/provider/ops/models.rs Mirrors the bearer_is_oauth precomputation pattern from factory.rs at the list-models call site; consistent with the rest of the fix.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant factory as resolve_cloud_slug / list_models
    participant helper as openai_bearer_is_oauth
    participant store as AuthProfilesStore
    participant routing as resolve_openai_codex_routing
    participant oauth as lookup_openai_oauth_credentials

    Caller->>factory: "request with slug=openai"
    factory->>helper: openai_bearer_is_oauth(config)
    helper->>store: get_profile(provider:openai)
    alt profile with credential found
        store-->>helper: Ok(Some(profile))
        helper-->>factory: "profile.kind == OAuth"
    else no stored profile with credential
        helper->>oauth: lookup_openai_oauth_credentials(config)
        oauth-->>helper: Some / None
        helper-->>factory: credentials present?
    end
    factory->>routing: resolve_openai_codex_routing(..., bearer_is_oauth)
    routing->>oauth: lookup_openai_oauth_credentials(config)
    oauth-->>routing: credentials with account_id
    alt bearer_is_oauth and credentials present
        routing-->>factory: "Codex backend, using_oauth=true"
    else
        routing-->>factory: "original endpoint, using_oauth=false"
    end
    factory-->>Caller: routed request
Loading

Reviews (1): Last reviewed commit: "fix(inference): route Codex OAuth chat t..." | Re-trigger Greptile

Comment thread src/openhuman/inference/provider/factory.rs
Comment thread src/openhuman/inference/provider/factory.rs

@M3gA-Mind M3gA-Mind left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Reviewed against upstream/main: CI green on every lane that applies to the changed areas, no unresolved review threads, and the diff does what the title and body claim. Checked correctness, blast radius on shared seams, that the behaviour change is pinned by a test rather than only asserted, and i18n/em-dash compliance on any new user-facing string. Nothing blocking found.

@M3gA-Mind
M3gA-Mind merged commit 41f455c into tinyhumansai:main Aug 5, 2026
24 of 25 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.

Codex CLI OAuth import routes chat through regular OpenAI API instead of Codex subscription

2 participants