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

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>
This commit is contained in:
j3ffffff 2026-04-17 02:31:38 -07:00 committed by Teknium
parent 227afcd80f
commit f76df30e08
2 changed files with 136 additions and 6 deletions

View file

@ -1547,12 +1547,21 @@ def refresh_codex_oauth_pure(
try:
err = response.json()
if isinstance(err, dict):
err_code = err.get("error")
if isinstance(err_code, str) and err_code.strip():
code = err_code.strip()
err_desc = err.get("error_description") or err.get("message")
if isinstance(err_desc, str) and err_desc.strip():
message = f"Codex token refresh failed: {err_desc.strip()}"
err_obj = err.get("error")
# OpenAI shape: {"error": {"code": "...", "message": "...", "type": "..."}}
if isinstance(err_obj, dict):
nested_code = err_obj.get("code") or err_obj.get("type")
if isinstance(nested_code, str) and nested_code.strip():
code = nested_code.strip()
nested_msg = err_obj.get("message")
if isinstance(nested_msg, str) and nested_msg.strip():
message = f"Codex token refresh failed: {nested_msg.strip()}"
# OAuth spec shape: {"error": "code_str", "error_description": "..."}
elif isinstance(err_obj, str) and err_obj.strip():
code = err_obj.strip()
err_desc = err.get("error_description") or err.get("message")
if isinstance(err_desc, str) and err_desc.strip():
message = f"Codex token refresh failed: {err_desc.strip()}"
except Exception:
pass
if code in {"invalid_grant", "invalid_token", "invalid_request"}: