Summary
When the Hermes fallback chain activates openai-codex with model gpt-5.4 (the documented Codex chat model), the request reaches the ChatGPT Codex backend with model name gpt-5-4 (dots replaced with hyphens) and gets HTTP 404. The Codex /responses endpoint accepts only gpt-5.4 (with the dot).
The conversion is not in hermes_cli/model_normalize.py — normalize_model_for_provider("gpt-5.4", "openai-codex") returns "gpt-5.4" correctly. The dot→hyphen rewrite is happening downstream of normalization, somewhere in the codex_responses request-build / fallback-activation path.
Environment
- Hermes Agent: latest (post
hermes update 2026-04-28)
- macOS 15.x arm64
- Provider chain (config.yaml):
model: claude-haiku-4-5-20251001
fallback_providers:
- provider: anthropic
model: claude-sonnet-4-6
- provider: openai-codex
model: gpt-5.4
- provider: openrouter
model: meta-llama/llama-3.3-70b-instruct:free
- Codex auth: working (token freshly refreshed via Codex CLI, pool entry
last_status: ok)
- Codex backend:
https://chatgpt.com/backend-api/codex (default)
Reproduction
- Configure
openai-codex with model gpt-5.4 anywhere in the chain.
- Trigger fallback (e.g. by sending a request when the primary is rate-limited, or by setting primary to
gpt-5.4 directly).
- Observe gateway log:
🔄 Primary model failed — switching to fallback: gpt-5.4 via openai-codex
⚠️ API call failed (attempt 1/3): NotFoundError [HTTP 404]
🔌 Provider: openai-codex Model: gpt-5.4
🌐 Endpoint: https://chatgpt.com/backend-api/codex
📝 Error: HTTP 404: model: gpt-5-4
Note the discrepancy: log line Model: gpt-5.4 (the configured value), but error message from the backend says gpt-5-4 — the wire-level model name was rewritten.
Confirmation that Codex backend itself works
Same machine, same OAuth token, same minute — direct curl to the Codex backend with gpt-5.4 returns 200:
TOKEN=$(python3 -c "import json,os; d=json.load(open(os.path.expanduser('~/.hermes/auth.json'))); print(d['credential_pool']['openai-codex'][0]['access_token'])")
curl -sN -X POST https://chatgpt.com/backend-api/codex/responses \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: text/event-stream" \
-H "OpenAI-Beta: responses=experimental" \
-d '{"model":"gpt-5.4","input":[{"role":"user","content":"Reply: WORKING"}],"instructions":"helpful","stream":true,"store":false}'
# → SSE stream, response.created with model: "gpt-5.4", deltas "WORK" + "ING"
Same payload with gpt-5 or gpt-5-codex:
{"detail":"The 'gpt-5-codex' model is not supported when using Codex with a ChatGPT account."}
So the backend strictly accepts gpt-5.4 (with the dot) and rejects the dash form.
What I've ruled out
model_normalize.py: I verified normalize_model_for_provider("gpt-5.4", "openai-codex") returns "gpt-5.4". openai-codex is in _STRIP_VENDOR_ONLY_PROVIDERS (dots preserved), not _DOT_TO_HYPHEN_PROVIDERS.
agent/anthropic_adapter.py: contains model.replace(".", "-") calls but only on the Anthropic path.
agent/model_metadata.py:1057: contains return model.replace(".", "-") but it's in a comparison helper, not the wire path.
Suspected location
Somewhere in the codex_responses fallback path:
run_agent.py _try_activate_fallback → constructs the responses request body
agent/auxiliary_client.py CodexAuxiliaryClient / resolve_provider_client Codex branch
- Or a model-comparison helper that normalizes both sides for matching but accidentally returns the normalized form to the wire layer
I don't have a definitive line number. Adding print statements just before the client.responses.stream() call would localize it quickly.
Related issues
These are not duplicates but share the same dot→hyphen rewrite pattern affecting different providers / paths:
If a single root cause lurks, fixing one might fix all four.
Impact
Anyone configuring openai-codex with gpt-5.4 (or any GPT-5.x with a dot) as a primary or fallback gets a 100% failure rate on that hop. Especially painful right now because Anthropic Sonnet/Opus on subscription OAuth is currently being gated by Anthropic (#17169), making openai-codex the natural backstop — which then 404s.
Workaround
None on the user side. The model name gpt-5-4 doesn't exist in Codex; gpt-5.4 is the canonical value. Direct API calls work; only Hermes-routed ones fail.
What might fix it
- Find the offending
.replace(".", "-") and gate it behind a provider check.
- Or replace it with the centralized
normalize_model_for_provider so the "preserve dots for _STRIP_VENDOR_ONLY_PROVIDERS" rule applies.
Happy to provide gateway logs, full request dumps, or run a focused trace.
Summary
When the Hermes fallback chain activates
openai-codexwith modelgpt-5.4(the documented Codex chat model), the request reaches the ChatGPT Codex backend with model namegpt-5-4(dots replaced with hyphens) and getsHTTP 404. The Codex/responsesendpoint accepts onlygpt-5.4(with the dot).The conversion is not in
hermes_cli/model_normalize.py—normalize_model_for_provider("gpt-5.4", "openai-codex")returns"gpt-5.4"correctly. The dot→hyphen rewrite is happening downstream of normalization, somewhere in the codex_responses request-build / fallback-activation path.Environment
hermes update2026-04-28)last_status: ok)https://chatgpt.com/backend-api/codex(default)Reproduction
openai-codexwith modelgpt-5.4anywhere in the chain.gpt-5.4directly).Note the discrepancy: log line
Model: gpt-5.4(the configured value), but error message from the backend saysgpt-5-4— the wire-level model name was rewritten.Confirmation that Codex backend itself works
Same machine, same OAuth token, same minute — direct
curlto the Codex backend withgpt-5.4returns 200:Same payload with
gpt-5orgpt-5-codex:So the backend strictly accepts
gpt-5.4(with the dot) and rejects the dash form.What I've ruled out
model_normalize.py: I verifiednormalize_model_for_provider("gpt-5.4", "openai-codex")returns"gpt-5.4".openai-codexis in_STRIP_VENDOR_ONLY_PROVIDERS(dots preserved), not_DOT_TO_HYPHEN_PROVIDERS.agent/anthropic_adapter.py: containsmodel.replace(".", "-")calls but only on the Anthropic path.agent/model_metadata.py:1057: containsreturn model.replace(".", "-")but it's in a comparison helper, not the wire path.Suspected location
Somewhere in the codex_responses fallback path:
run_agent.py_try_activate_fallback→ constructs the responses request bodyagent/auxiliary_client.pyCodexAuxiliaryClient/resolve_provider_clientCodex branchI don't have a definitive line number. Adding
printstatements just before theclient.responses.stream()call would localize it quickly.Related issues
These are not duplicates but share the same dot→hyphen rewrite pattern affecting different providers / paths:
.→-)If a single root cause lurks, fixing one might fix all four.
Impact
Anyone configuring
openai-codexwithgpt-5.4(or any GPT-5.x with a dot) as a primary or fallback gets a 100% failure rate on that hop. Especially painful right now because Anthropic Sonnet/Opus on subscription OAuth is currently being gated by Anthropic (#17169), makingopenai-codexthe natural backstop — which then 404s.Workaround
None on the user side. The model name
gpt-5-4doesn't exist in Codex;gpt-5.4is the canonical value. Direct API calls work; only Hermes-routed ones fail.What might fix it
.replace(".", "-")and gate it behind a provider check.normalize_model_for_providerso the "preserve dots for_STRIP_VENDOR_ONLY_PROVIDERS" rule applies.Happy to provide gateway logs, full request dumps, or run a focused trace.