fix: add ChatGPT-Account-ID and User-Agent headers for openai-codex provider#5169
Closed
lesterli wants to merge 1 commit into
Closed
fix: add ChatGPT-Account-ID and User-Agent headers for openai-codex provider#5169lesterli wants to merge 1 commit into
lesterli wants to merge 1 commit into
Conversation
…rovider
Requests to chatgpt.com/backend-api/codex are blocked by Cloudflare when
ChatGPT-Account-ID header and a recognisable User-Agent are missing. The
agent receives an HTML JS-challenge page instead of JSON, causing silent
failures logged as "Non-retryable client error".
This adds a cached `codex_default_headers()` function in hermes_cli/auth.py
that provides:
- User-Agent: hermes-agent/{version} ({os} {release}; {arch})
- ChatGPT-Account-ID: {account_id from auth store tokens}
Headers are injected at all 5 code paths that create OpenAI clients for the
Codex backend (main agent init, credential refresh, credential swap,
auxiliary client, and raw provider client).
Likely also addresses:
- #2176 (account bans — requests without account header may be flagged)
- #1167 (usage shown as "Other" — no client identification headers)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
saamuelng601-pixel
added a commit
to saamuelng601-pixel/hermes-agent
that referenced
this pull request
Apr 16, 2026
ChatGPT's Codex backend sits behind Cloudflare's managed challenge. When Hermes hits /responses or /chat/completions via the Python openai SDK, Cloudflare sees the default "OpenAI/Python X.X.X" User-Agent and the absence of ChatGPT-Account-Id, classifies the request as bot traffic, and returns an HTML JS-challenge page that the openai SDK cannot handle. Hermes reports this as "Non-retryable client error: <html>..." and falls back to the secondary provider even when the Codex credentials themselves are fresh and the account is under-quota. The official Rust codex_cli_rs CLI sends three headers that Cloudflare uses to verify the client: - User-Agent: codex_cli_rs/<version> - originator: codex_cli_rs - ChatGPT-Account-Id: <decoded from JWT> This change installs an httpx.Client.send / AsyncClient.send wrapper that inspects the outbound request, detects chatgpt.com hosts, and injects the three headers (decoding ChatGPT-Account-Id from the bearer JWT). This covers every OpenAI client Hermes creates — including auxiliary flows (_try_codex, resolve_provider_client, _to_async_client) that PR NousResearch#6391 doesn't reach. Tested locally: requests to /responses now return 200 (previously HTML 403). /chat/completions returns 404 (backend doesn't expose that route for Codex), which is the expected behavior — callers should use the Responses API adapter. Refs: NousResearch#6391 (upstream CF fix, scoped to main agent only), NousResearch#5169 (closed earlier attempt), NousResearch#10016 (browser-compat Codex login roadmap).
1 task
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
Requests to
chatgpt.com/backend-api/codexare silently blocked by Cloudflare'smanaged challenge when
ChatGPT-Account-IDand a recognisableUser-Agentaremissing. The agent receives an HTML JS-challenge page instead of JSON, logged as
Non-retryable client error: <html>…Enable JavaScript and cookies….This PR adds both required headers to every OpenAI client targeting the Codex backend.
Root cause
The Codex CLI (
codex_cli_rs) sendsChatGPT-Account-IDand a versionedUser-Agenton every request. Hermes only sendsAuthorization: Bearer— noaccount header, no identifiable user-agent. Cloudflare's bot-detection layer on
chatgpt.comthen serves a JS challenge page that the OpenAI Python SDK cannothandle.
This likely also explains:
ChatGPT-Account-IDmay be classified as unauthorised API abuse)identification headers)
Changes
hermes_cli/auth.pyaccount_idfrom auth store tokens; new cachedcodex_default_headers()functionrun_agent.pyagent/auxiliary_client.py_try_codex(),resolve_provider_client()Headers sent
The
account_idis already stored in~/.hermes/auth.jsonunderproviders.openai-codex.tokens.account_id(populated during OAuth loginand Codex CLI credential migration).
Design decisions
force_refresh=Trueis only called after credential rotation in
_try_refresh_codex_client_credentials.hermes_cli/auth.py: Follows the pattern ofcopilot_default_headers()in
hermes_cli/models.py— provider-specific header builders live near theirauth logic.
AuthError,FileNotFoundError,KeyError(not bare
Exception), withlogger.debugfor visibility.hermes_cli.__version__: Avoids hardcoded version strings.Test plan
model.provider: openai-codexand send a message — response received(previously: Cloudflare HTML block → silent failure)
python -c "from hermes_cli.auth import codex_default_headers; print(codex_default_headers())"force_refresh=Truerebuilds headersChatGPT-Account-ID🤖 Generated with Claude Code