fix(3.1.10): heal legacy openai/<gpt> configs on startup (OAuth 401 Missing bearer) - #266
Conversation
ChatGPT-subscription boxes (Codex OAuth, no OpenAI API key) that stored their active model or a fallback as `openai/gpt-5.5` (etc.) before the setup UI routed ChatGPT picks through Codex hit `401 Missing bearer or basic authentication in header` on api.openai.com/v1/responses — often only as a FailoverError days into use, once the OAuth token first refreshes and the failover chain reaches the keyless `openai/*` fallback. The chat-model pick route already rewrites openai/<gpt> -> codex/<gpt>, but only when the user re-picks the model; existing configs never re-pick, so an updated box stays broken until manually re-selected. Migrate primary + fallbacks in gateway-pre-start.sh on gateway start, guarded on "codex OAuth present AND no OpenAI API key" so keyed / dual-auth boxes (where openai/* is a valid route) are left untouched. Mirrors CODEX_SUPPORTED_MODEL_RE / hasOpenAiApiKeyProfile / hasCodexOauthProfile in src/app/setup-api/chat/model/route.ts. Verified against 6 fixtures: openai->codex primary+fallback migrate; keyed box untouched; non-supported (gpt-4o) primary left as-is; already-codex no-op; no-codex-auth untouched; composes with the retired-Sonnet migration. bash -n and py_compile pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
📝 WalkthroughWalkthroughThe gateway pre-start migration now translates supported ChangesCodex model migration
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
🦀 ClawReviewFresh PR washed in with the tide — here's the gist. This PR fixes a silent 401 ('Missing bearer') that hit ChatGPT-subscription users who updated from 3.1.5+: their device kept At a glance
Good to know
— ClawReview 🦀, scuttling off. General info only — see CodeRabbit for the detailed review. Conventions: docs. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@scripts/gateway-pre-start.sh`:
- Around line 188-195: Update _has_codex_oauth_profile to recognize both “codex”
and the legacy “openai-codex” provider values when mode is “oauth”. Preserve the
existing profile filtering and normalization behavior so either provider
triggers the migration.
🪄 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: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b397a7dd-9a27-4cdd-8216-eaf672600095
📒 Files selected for processing (1)
scripts/gateway-pre-start.sh
| def _has_codex_oauth_profile(): | ||
| for _entry in _auth_profiles(): | ||
| if not isinstance(_entry, dict): | ||
| continue | ||
| _p = str(_entry.get("provider", "")).strip().lower() | ||
| _m = str(_entry.get("mode", "")).strip().lower() | ||
| if _p == "codex" and _m == "oauth": | ||
| return True |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Recognize legacy openai-codex OAuth profiles.
This only accepts provider == "codex", although this script still treats openai-codex as a legacy Codex provider. Those OAuth-only devices will skip this migration and retain the openai/<gpt> IDs that cause the reported 401.
Proposed fix
- if _p == "codex" and _m == "oauth":
+ if _p in ("codex", "openai-codex") and _m == "oauth":
return True📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def _has_codex_oauth_profile(): | |
| for _entry in _auth_profiles(): | |
| if not isinstance(_entry, dict): | |
| continue | |
| _p = str(_entry.get("provider", "")).strip().lower() | |
| _m = str(_entry.get("mode", "")).strip().lower() | |
| if _p == "codex" and _m == "oauth": | |
| return True | |
| def _has_codex_oauth_profile(): | |
| for _entry in _auth_profiles(): | |
| if not isinstance(_entry, dict): | |
| continue | |
| _p = str(_entry.get("provider", "")).strip().lower() | |
| _m = str(_entry.get("mode", "")).strip().lower() | |
| if _p in ("codex", "openai-codex") and _m == "oauth": | |
| return True |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@scripts/gateway-pre-start.sh` around lines 188 - 195, Update
_has_codex_oauth_profile to recognize both “codex” and the legacy “openai-codex”
provider values when mode is “oauth”. Preserve the existing profile filtering
and normalization behavior so either provider triggers the migration.
* Harden gateway recovery after updates (#263) * fix: migrate legacy openai/<gpt> model+fallbacks to codex on startup (#266) ChatGPT-subscription boxes (Codex OAuth, no OpenAI API key) that stored their active model or a fallback as `openai/gpt-5.5` (etc.) before the setup UI routed ChatGPT picks through Codex hit `401 Missing bearer or basic authentication in header` on api.openai.com/v1/responses — often only as a FailoverError days into use, once the OAuth token first refreshes and the failover chain reaches the keyless `openai/*` fallback. The chat-model pick route already rewrites openai/<gpt> -> codex/<gpt>, but only when the user re-picks the model; existing configs never re-pick, so an updated box stays broken until manually re-selected. Migrate primary + fallbacks in gateway-pre-start.sh on gateway start, guarded on "codex OAuth present AND no OpenAI API key" so keyed / dual-auth boxes (where openai/* is a valid route) are left untouched. Mirrors CODEX_SUPPORTED_MODEL_RE / hasOpenAiApiKeyProfile / hasCodexOauthProfile in src/app/setup-api/chat/model/route.ts. Verified against 6 fixtures: openai->codex primary+fallback migrate; keyed box untouched; non-supported (gpt-4o) primary left as-is; already-codex no-op; no-codex-auth untouched; composes with the retired-Sonnet migration. bash -n and py_compile pass. Co-authored-by: Mike (IDRobots) <mike@idrobots.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> * fix(3.1.10): responsive chat header pills (wrap instead of overlap on narrow chat) (#267) * fix: chat header pills squeeze + truncate cleanly on narrow panels On a narrow chat the provider / model / thinking selector pills overlapped into an unreadable strip. Two parts: 1. .header-dropdown-trigger gets width:100% so the button fills its flex-shrinking .header-dropdown parent. Previously the button kept its content width and spilled past the shrunk parent, so overflow:hidden on .chat-header-pills clipped / overlapped the pills instead of the labels truncating. Now every pill gives ground evenly and its label ellipsizes (the chevron stays — it's reserved in the 24px right padding). 2. Single row (no wrap) + overflow:hidden, and the chat window clamps to MIN_CHAT_WIDTH (340px) on both resize paths + the rendered width, so the window stops shrinking once the pills reach a readable minimum instead of smashing them. The open menu is portaled to <body> (HeaderDropdown), so clipping the row can't hide it. Verified at 320-420px: even truncation, carets visible, zero overlap. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: open chat from mascot with a macOS-style animation, no corner flash - Stop streaming the frozen mascot's position into mascotX while the chat is open (page.tsx). That nudged mascotX for a frame right after opening, so the popup flashed to the wrong corner before settling. mascotX is now captured once from the tap. - Grow the popup OUT of the mascot: transform-origin pinned to the popup's bottom edge, aligned horizontally with the mascot, and scale 0.82 -> 1 on an easeOutExpo curve (cubic-bezier(0.16,1,0.3,1)) over 0.36s. Smooth, premium, emanates from where you tapped instead of scaling from the popup centre. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * chore: bump SW cache clawbox-v3 -> v4 to invalidate stale assets on the 3.1.10 chat-UI changes Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix: keep chat popup header on-screen on short/zoomed viewports The un-dragged popup anchors from the bottom (bottom:170 above the mascot, bottom:65 in tray mode) but its maxHeight budget was a flat 100vh-60px, so on viewports shorter than ~680px (small windows, browser zoom) a 500px-tall popup shoved its whole header — pills, status dot, close button — off the TOP of the screen (rect.y = -76 measured on a 594px viewport). Subtract the bottom anchor from the height budget per mode (+12px top margin) so the header is always visible and the popup just gets shorter instead. Found by driving the real desktop over CDP and sampling the popup rect during open; the same probe confirmed the mascot-open animation runs and there is no left-corner flash. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Mike (IDRobots) <mike@idrobots.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Mike (IDRobots) <mike@idrobots.com> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Problem (customer report, box on 3.1.5)
FailoverError: 401 Unauthorized: Missing bearer or basic authentication in header, url: https://api.openai.com/v1/responses— ChatGPT (OAuth) user, no API key. Worked for days, then died.Root cause
The device stored
agents.defaults.model.primary(and/or afallbacksentry) asopenai/gpt-5.5from before the setup UI routed ChatGPT picks through Codex. With Codex-OAuth auth and no OpenAI API key,openai/*resolves toapi.openai.comwith no bearer → 401. It surfaces as a FailoverError days in because the codex primary works until the OAuth token first refreshes, then failover reaches the keylessopenai/*fallback.src/app/setup-api/chat/model/route.ts(shipped v3.1.7) already rewritesopenai/<gpt>→codex/<gpt>— but only when the user re-picks the model. Existing configs never re-pick, so a box updated 3.1.5 → 3.1.9 stays broken until the model is manually re-selected. That's the gap this closes.Fix
Extend the startup migration in
scripts/gateway-pre-start.sh(right after the retired-Sonnet migration) to rewriteopenai/<codex-supported gpt>→codex/<gpt>for primary + every fallback, guarded on codex OAuth present AND no OpenAI API key — so keyed / dual-auth boxes (whereopenai/*is valid) are untouched. MirrorsCODEX_SUPPORTED_MODEL_RE/hasOpenAiApiKeyProfile/hasCodexOauthProfilefrom the chat-model route.Testing (6 fixtures, real extracted block)
openai/gpt-5.5+ fallbackopenai/gpt-5.4codex/gpt-5.5,codex/gpt-5.4✅openai/gpt-4oprimarycodex/gpt-5.5bash -nandpython3 -m py_compilepass. Onlyscripts/gateway-pre-start.shchanged (+64 lines).Note for release assembly
Targets beta (3.1.10), which already carries the routing fix + gateway-recovery hardening (#263). Pending on-box smoke test (box 192.168.50.144) before promoting to
main.🤖 Generated with Claude Code
Summary by CodeRabbit