Skip to content

fix(codex): restore the credential #278 broke, and migrate legacy auth profiles - #279

Merged
yalexx merged 1 commit into
betafrom
fix/codex-mirror-needs-refresh-token
Jul 28, 2026
Merged

yalexx merged 1 commit into
betafrom
fix/codex-mirror-needs-refresh-token

Conversation

@yalexx

@yalexx yalexx commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

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:

if (typeof refreshToken !== "string" || !refreshToken) return null;

A null credential 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.

File Read by Rotated by Now
~/.codex/auth.json codex plugin nothing written, with refresh token
<agent>/codex-home/auth.json Codex app-server the app-server not written; deleted if present

Core pushes the app-server its tokens over account/login/start, so that second file was never needed. Still exactly one rotator, so the original 401 refresh_token_reused burn 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 the auth_profile_store table of openclaw-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.js copies 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=- to profile=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:

/backend-api/responses         403  Cloudflare challenge page
/backend-api/codex/responses   400  {"detail":"Input must be a list"}   <- auth accepted

Not fixable here: overriding models.providers.codex.baseUrl corrects the URL but then the payload shape is wrong (reason=format). ⚠️ Do not tag 3.1.11 — codex is broken for ChatGPT users regardless of our code, and a tag reaches the whole fleet.

🤖 Generated with Claude Code

…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>
@yalexx
yalexx requested a review from a team as a code owner July 28, 2026 11:35
@github-actions

Copy link
Copy Markdown

🦀 ClawReview

Claws 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

  • 🔧 Fix · touches Codex auth credential mirror, gateway-pre-start.sh, new SQLite auth-profile migration script
  • Base branch: beta · +172 source / +220 tests across 5 files
  • ✅ base beta matches the beta-first convention
  • ✅ conventional PR title
  • ℹ️ touches security-sensitive paths (scripts/gateway-pre-start.sh) — review with extra care

Good to know

  • ℹ️ gateway-pre-start.sh now runs the migration on every gateway start before the mirror — the script is designed to be idempotent and exits 0 on failure so it never blocks startup.
  • 🟡 migrate-auth-profiles.js writes directly to openclaw-agent.sqlite, touching live customer auth state. Copy-don't-move and never-clobber semantics are in place.
  • 🟡 PR author explicitly flags: do not tag 3.1.11 — Codex remains broken for ChatGPT users due to a wrong API endpoint in core outside ClawBox's control, and a tag would reach the whole fleet.
  • ℹ️ 22 new or rewritten tests (13 mirror + 9 migration), full suite at 1490 passed across 124 files.

— ClawReview 🦀, your resident reef crab. Just orientation — CodeRabbit does the line-by-line, humans do the merge. Conventions: docs.

@github-actions github-actions Bot added the area: install Auto-triage area label Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown

CI Summary

✅ Tests

  • Result: passed
  • View run
  • Coverage: statements 70.06%, branches 60.06%, functions 65.41%, lines 72.03%

✅ E2E

✅ E2E Install

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

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: 22 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: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: e791b5d4-fd22-47e3-8d82-e96fd5130707

📥 Commits

Reviewing files that changed from the base of the PR and between e403dbc and b5d5d22.

📒 Files selected for processing (5)
  • scripts/codex-auth-mirror.js
  • scripts/gateway-pre-start.sh
  • scripts/migrate-auth-profiles.js
  • src/tests/unit/codex-auth-mirror.test.ts
  • src/tests/unit/migrate-auth-profiles.test.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/codex-mirror-needs-refresh-token

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

@yalexx
yalexx merged commit 43f1089 into beta Jul 28, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: install Auto-triage area

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant