fix(codex): restore the credential #278 broke, and migrate legacy auth profiles - #279
Conversation
…h profiles Two fixes for the same symptom -- a box signed in with ChatGPT where core attaches no auth at all (`profile=-` in the gateway log) and every turn 401s while the UI still shows the provider connected. 1. #278 stripped refresh_token from the credential mirrors. Core's readCodexCliCredentials() hard-rejects a credential without one: if (typeof refreshToken !== "string" || !refreshToken) return null; A null credential means the codex plugin attaches nothing. The rotation fix was right about the danger and wrong about the remedy: safety comes from WHERE the credential is written, not from omitting the field. So the token goes back into ~/.codex/auth.json -- which the plugin reads and never writes, and which no process opens as CODEX_HOME -- while <agentDir>/codex-home/auth.json, the copy the Codex app-server rotates, is no longer written at all and is deleted if 3.1.11 left one behind. Core pushes the app-server its tokens over account/login/start, so that file was never needed. Still exactly one rotator, so the original 401 refresh_token_reused burn stays fixed. 2. Credentials written by the setup wizard can land only in the legacy <agentDir>/auth-profiles.json, while core 2026.7.x resolves auth from the auth_profile_store table of openclaw-agent.sqlite. Observed on a factory-fresh box on 2026-07-28: three profiles in the JSON file, ZERO rows in sqlite. scripts/migrate-auth-profiles.js copies them across on every gateway start, before the mirror runs so it reads a populated store. Copy-don't-move and never-clobber: the JSON stays for a core downgrade, and anything already in sqlite is the live credential and wins. Verified on the live box: with both applied the gateway went from `profile=-` to `profile=sha256:6be7b650...`, i.e. auth resolves and is attached. Codex still fails afterwards, for an unrelated core bug: core sends the request to https://chatgpt.com/backend-api/responses (a browser endpoint Cloudflare managed-challenges) instead of /backend-api/codex/responses. Proven from the box with its own token, same second: the codex path returns a normal JSON API response while the path core uses returns a 403 challenge page. Not fixable here -- overriding models.providers.codex.baseUrl corrects the URL but then the payload shape is wrong (reason=format). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
🦀 ClawReviewClaws waving — here's what this change is about. Fixes two layered auth failures that together cause ChatGPT/Codex to show as 'connected' in the UI while every API turn silently 401s. The first fix restores the refresh_token field to ~/.codex/auth.json (which core hard-requires to attach any auth at all) while removing the app-server's rotating copy that was burning token families. The second adds a migration script that copies credentials from the legacy auth-profiles.json into the auth_profile_store SQLite table that core 2026.7.x actually reads at runtime — a gap observed on factory-fresh boxes with three profiles in JSON and zero rows in sqlite. At a glance
Good to know
— ClawReview 🦀, your resident reef crab. Just orientation — CodeRabbit does the line-by-line, humans do the merge. Conventions: docs. |
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 22 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (5)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Two fixes for the same symptom: a box signed in with ChatGPT where core attaches no auth at all (
profile=-in the gateway log) and every turn 401s — while the UI still shows the provider as connected.1. #278 stripped a field core requires
readCodexCliCredentials()in core hard-rejects a credential without a refresh token:A
nullcredential means the codex plugin attaches nothing. #278 was right about the danger and wrong about the remedy — safety comes from where the credential is written, not from omitting the field.~/.codex/auth.json<agent>/codex-home/auth.jsonCore pushes the app-server its tokens over
account/login/start, so that second file was never needed. Still exactly one rotator, so the original401 refresh_token_reusedburn stays fixed.2. Credentials landing in the legacy store
The setup wizard can leave the credential only in
<agentDir>/auth-profiles.json, while core 2026.7.x resolves auth from theauth_profile_storetable ofopenclaw-agent.sqlite. Core itself calls the JSON file legacy ("Repair legacy auth-profiles.json files").On a factory-fresh box on 2026-07-28: three profiles in JSON, zero rows in sqlite.
scripts/migrate-auth-profiles.jscopies them across on every gateway start, before the mirror runs so it reads a populated store. Copy-don't-move (a core downgrade still finds the JSON) and never-clobber (anything already in sqlite is the live credential and wins).Verification
Live box: with both applied the gateway went from
profile=-toprofile=sha256:6be7b650…— auth resolves and is attached. Both scripts re-run as clean no-ops on an already-correct box.Tests: 13 rewritten mirror tests (refresh token present, rotating copy never written, 3.1.11 leftovers removed, sqlite path, idempotence, perms) + 9 new migration tests. Full suite 1490 passed / 124 files.
Known remaining failure — not this PR, not our code
Codex still fails afterwards. Core sends the request to
https://chatgpt.com/backend-api/responses, a browser endpoint Cloudflare managed-challenges, instead of/backend-api/codex/responses. Proven from the box with its own token, same second:Not fixable here: overriding⚠️ Do not tag 3.1.11 — codex is broken for ChatGPT users regardless of our code, and a tag reaches the whole fleet.
models.providers.codex.baseUrlcorrects the URL but then the payload shape is wrong (reason=format).🤖 Generated with Claude Code