fix(auth): parse OpenAI nested error shape in Codex token refresh#11512
Closed
j3ffffff wants to merge 1 commit into
Closed
fix(auth): parse OpenAI nested error shape in Codex token refresh#11512j3ffffff wants to merge 1 commit into
j3ffffff wants to merge 1 commit into
Conversation
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>
Collaborator
Contributor
|
Thanks for this fix, @j3ffffff! The nested OpenAI error shape parser from this PR has landed on 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. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
hermes_cli/auth.py:refresh_codex_oauth_purefails to surface the correcterrorcode/message when OpenAI returns a 401refresh_token_reused— the existing dedicated branch (with actionable re-auth guidance andrelogin_required=True) never fires.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:
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:
…with
relogin_required=Trueso 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/typeis mapped onto the samecodevariable so the existing branches at lines ~1253-1262 work without change.Test plan
tests/hermes_cli/test_auth_codex_provider.py:refresh_token_reused→code,relogin_required=True, actionable messageinvalid_client) → code + message surfacedinvalid_grant→ back-compat, still triggersrelogin_required=True"status 401"fallback,relogin_required=False🤖 Generated with Claude Code