Skip to content

fix(auth): parse OpenAI nested error shape in Codex token refresh#11512

Closed
j3ffffff wants to merge 1 commit into
NousResearch:mainfrom
j3ffffff:fix/codex-refresh-parser-error-shape
Closed

fix(auth): parse OpenAI nested error shape in Codex token refresh#11512
j3ffffff wants to merge 1 commit into
NousResearch:mainfrom
j3ffffff:fix/codex-refresh-parser-error-shape

Conversation

@j3ffffff

Copy link
Copy Markdown
Contributor

Summary

  • Codex token refresh at hermes_cli/auth.py:refresh_codex_oauth_pure fails to surface the correct error code/message when OpenAI returns a 401 refresh_token_reused — the existing dedicated branch (with actionable re-auth guidance and relogin_required=True) never fires.
  • Root cause: OpenAI returns errors as a nested object, not the OAuth spec's flat shape. The parser only understood the flat shape.
  • Fix: handle both shapes; downstream code-based branches (refresh_token_reused, invalid_grant, invalid_token, invalid_request) now fire correctly.

Before

User's access token expires, hermes attempts refresh, another Codex client (Codex CLI, VS Code extension) had already consumed the single-use refresh token. User sees:

Provider authentication failed: Codex token refresh failed with status 401.

No indication that re-authentication is required. Every subsequent message fails the same way.

After

Same scenario now surfaces the existing (but previously unreachable) message:

Codex refresh token was already consumed by another client (e.g. Codex CLI or VS Code extension). Run codex in your terminal to generate fresh tokens, then run hermes auth to re-authenticate.

…with relogin_required=True so callers can prompt re-auth instead of silently retrying.

The two error shapes

OpenAI (https://auth.openai.com/oauth/token):

{\"error\": {\"code\": \"refresh_token_reused\", \"message\": \"...\", \"type\": \"invalid_request_error\"}}

OAuth 2.0 spec (what the old parser expected):

{\"error\": \"invalid_grant\", \"error_description\": \"...\"}

Both are now handled. The nested code/type is mapped onto the same code variable so the existing branches at lines ~1253-1262 work without change.

Test plan

  • New unit tests in tests/hermes_cli/test_auth_codex_provider.py:
    • nested refresh_token_reusedcode, relogin_required=True, actionable message
    • nested generic code (invalid_client) → code + message surfaced
    • flat OAuth-spec invalid_grant → back-compat, still triggers relogin_required=True
    • unparseable body → generic "status 401" fallback, relogin_required=False
  • All 19 tests in the file pass locally.

🤖 Generated with Claude Code

OpenAI's OAuth token endpoint returns errors in a nested shape —
{"error": {"code": "refresh_token_reused", "message": "..."}} —
not the OAuth spec's flat {"error": "...", "error_description": "..."}.
The existing parser only handled the flat shape, so:

- `err.get("error")` returned a dict, the `isinstance(str)` guard
  rejected it, and `code` stayed `"codex_refresh_failed"`.
- The dedicated `refresh_token_reused` branch (with its actionable
  "re-run codex + hermes auth" message and `relogin_required=True`)
  never fired.
- Users saw the generic "Codex token refresh failed with status 401"
  when another Codex client (CLI, VS Code extension) had consumed
  their single-use refresh token — giving no hint that re-auth was
  required.

Parse both shapes, mapping OpenAI's nested `code`/`type` onto the
existing `code` variable so downstream branches (`refresh_token_reused`,
`invalid_grant`, etc.) fire correctly.

Add regression tests covering:
- nested `refresh_token_reused` → actionable message + relogin_required
- nested generic code → code + message surfaced
- flat OAuth-spec `invalid_grant` still handled (back-compat)
- unparseable body → generic fallback message, relogin_required=False

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@alt-glitch alt-glitch added type/bug Something isn't working P1 High — major feature broken, no workaround area/auth Authentication, OAuth, credential pools labels Apr 24, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Likely superseded by #15104 (merged) which consolidated this fix along with #5948 and #4522. The nested error shape parsing from this PR was included in that salvage PR.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for this fix, @j3ffffff! The nested OpenAI error shape parser from this PR has landed on main — this is an automated hermes-sweeper review confirming it's implemented.

The fix was included in the consolidated salvage PR #15104 (fix(codex): consolidated OAuth error parsing + failed-status fallback routing + reauth UX), merged 2026-04-24, which credited your work explicitly.

Evidence:

Closing as superseded by the merged consolidation. Your contribution is preserved in the commit history.

@teknium1 teknium1 closed this Apr 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools P1 High — major feature broken, no workaround type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants