Summary
The openai-codex provider chat path returns HTTP 401 "Missing Authentication header" on every request, even with a freshly-issued OAuth token. Reproduces on both v0.13.0 and v0.14.0. The bug is in the chat request-builder — the Authorization: Bearer <token> header is not attached, despite the access_token being present in the credential pool.
The token itself is valid: direct curl with the same token (extracted from ~/.hermes/profiles/<profile>/auth.json) reaches the endpoint and gets HTTP 400 "Instructions are required" — i.e., auth accepted, body validation failed, which proves the token works.
The auxiliary client (agent/auxiliary_client.py) appears to correctly attach the header via _pool_runtime_api_key() → OpenAI(api_key=...). The chat-command path uses a different code path that doesn't go through this flow.
Repro
- Authenticate with openai-codex via device code:
hermes -p <profile> auth add openai-codex --type oauth --no-browser
- Confirm auth is healthy:
hermes -p <profile> auth status openai-codex → "openai-codex: logged in"
- Run a one-shot chat:
hermes -p <profile> chat -Q --source test -q "ping"
Expected: chat completes, model responds.
Actual: Error: Error code: 401 - {'error': {'message': 'Missing Authentication header', 'code': 401}}
Versions affected
- v0.14.0 (PyPI latest, 2026-05-16) — emits this 401 in 0.13, but in 0.14 the response shape change downstream produces
TypeError: 'NoneType' object is not iterable (Non-retryable client error). Same root cause: chat path drops the auth header.
- v0.13.0 (2026-05-07) — emits the bare 401 visibly.
Diagnostic data
auth.json credential_pool["openai-codex"][0] has all expected fields: access_token (1879-char JWT), refresh_token, base_url: https://chatgpt.com/backend-api/codex, auth_type: oauth. The JWT exp is 10 days in the future. The JWT's chatgpt_account_id claim is present.
- Direct curl test against
https://chatgpt.com/backend-api/codex/responses with the same token + Authorization: Bearer <token> + ChatGPT-Account-ID: <acct> + User-Agent: codex_cli_rs/0.0.0 returns HTTP 400 with {"detail":"Instructions are required"}. Token works server-side; client doesn't attach it.
_read_codex_access_token() in agent/auxiliary_client.py:1324 reads correctly from the pool, but the chat-command code path doesn't appear to use it.
- Trying
OPENAI_API_KEY env var as a workaround does not bypass — same 401.
- Patching
last_status: null → "ok" on the pool entry does not bypass.
Environment
- Windows 11
- Hermes installed via pip in venv at
~/AppData/Local/hermes/hermes-agent
- Python 3.11.15
- OpenAI Codex via OAuth (no API key)
- ChatGPT Pro account
Note on OpenAI API change
Endpoint now requires an instructions field in the request body. The 400 from direct curl proves this. If Hermes's request builder sends without instructions, this would be a separate downstream bug. But the 401 in the chat path indicates we're not even reaching the validation layer — the request is unauthenticated at the wire.
Summary
The
openai-codexprovider chat path returns HTTP 401 "Missing Authentication header" on every request, even with a freshly-issued OAuth token. Reproduces on both v0.13.0 and v0.14.0. The bug is in the chat request-builder — theAuthorization: Bearer <token>header is not attached, despite the access_token being present in the credential pool.The token itself is valid: direct curl with the same token (extracted from
~/.hermes/profiles/<profile>/auth.json) reaches the endpoint and gets HTTP 400 "Instructions are required" — i.e., auth accepted, body validation failed, which proves the token works.The auxiliary client (
agent/auxiliary_client.py) appears to correctly attach the header via_pool_runtime_api_key()→OpenAI(api_key=...). The chat-command path uses a different code path that doesn't go through this flow.Repro
hermes -p <profile> auth status openai-codex→ "openai-codex: logged in"hermes -p <profile> chat -Q --source test -q "ping"Expected: chat completes, model responds.
Actual:
Error: Error code: 401 - {'error': {'message': 'Missing Authentication header', 'code': 401}}Versions affected
TypeError: 'NoneType' object is not iterable(Non-retryable client error). Same root cause: chat path drops the auth header.Diagnostic data
auth.jsoncredential_pool["openai-codex"][0]has all expected fields:access_token(1879-char JWT),refresh_token,base_url: https://chatgpt.com/backend-api/codex,auth_type: oauth. The JWTexpis 10 days in the future. The JWT'schatgpt_account_idclaim is present.https://chatgpt.com/backend-api/codex/responseswith the same token +Authorization: Bearer <token>+ChatGPT-Account-ID: <acct>+User-Agent: codex_cli_rs/0.0.0returns HTTP 400 with{"detail":"Instructions are required"}. Token works server-side; client doesn't attach it._read_codex_access_token()inagent/auxiliary_client.py:1324reads correctly from the pool, but the chat-command code path doesn't appear to use it.OPENAI_API_KEYenv var as a workaround does not bypass — same 401.last_status: null→"ok"on the pool entry does not bypass.Environment
~/AppData/Local/hermes/hermes-agentNote on OpenAI API change
Endpoint now requires an
instructionsfield in the request body. The 400 from direct curl proves this. If Hermes's request builder sends withoutinstructions, this would be a separate downstream bug. But the 401 in the chat path indicates we're not even reaching the validation layer — the request is unauthenticated at the wire.