hermes - 💡(How to fix) Fix Anthropic fallback can fail after Codex reasoning-only empty turns due to trailing assistant prefill [1 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

When a session starts on the Codex Responses API and then falls back to native Anthropic after repeated empty visible responses, Hermes can send Anthropic a message list ending in an assistant turn. Anthropic rejects that as unsupported assistant prefill:

This model does not support assistant message prefill. The conversation must end with a user message.

This is not an Anthropic credential/configuration failure. The fallback activates and the request body is in Anthropic Messages shape, but Codex-specific empty reasoning-only assistant turns survive the pre-call sanitizer and are converted into assistant "(empty)" blocks.

Root Cause

This makes Anthropic fallback look broken even though native Anthropic support and credentials are otherwise valid. It also makes debugging harder because the synthetic request dump URL currently points to /chat/completions, while the body shape is Anthropic Messages API style.

Fix Action

Fixed

Code Example

This model does not support assistant message prefill. The conversation must end with a user message.

---

{
  "role": "assistant",
  "content": "",
  "finish_reason": "incomplete",
  "codex_reasoning_items": [
    {"type": "reasoning", "encrypted_content": "..."}
  ]
}

---

Fallback activated: gpt-5.5 → claude-opus-4-7 (anthropic)

---

[
  {"role": "user", "content": [{"type": "text", "text": "..."}]},
  {"role": "assistant", "content": [
    {"type": "text", "text": "(empty)"},
    {"type": "text", "text": "(empty)"}
  ]}
]
RAW_BUFFERClick to expand / collapse

Summary

When a session starts on the Codex Responses API and then falls back to native Anthropic after repeated empty visible responses, Hermes can send Anthropic a message list ending in an assistant turn. Anthropic rejects that as unsupported assistant prefill:

This model does not support assistant message prefill. The conversation must end with a user message.

This is not an Anthropic credential/configuration failure. The fallback activates and the request body is in Anthropic Messages shape, but Codex-specific empty reasoning-only assistant turns survive the pre-call sanitizer and are converted into assistant "(empty)" blocks.

Observed environment

  • Hermes local checkout: 39c41d0f23a35fdecc143e3cc5ffb2b4dbd3e25d
  • origin/main checked at: 5e743559e0157df42e0f640cd06d736e898370d0
  • Primary provider/model: openai-codex / gpt-5.5
  • Fallback provider/model: anthropic / claude-opus-4-7
  • Failure mode repeated across multiple scheduled turns in the same session.

I also checked the relevant code paths on origin/main; _is_thinking_only_assistant() still appears to ignore codex_reasoning_items, and dump_api_request_debug() still renders non-Codex endpoints as /chat/completions in the debug URL.

What happened

  1. The primary Codex Responses model returned no visible assistant content, but did return encrypted reasoning state.
  2. Hermes built and persisted assistant messages shaped roughly like:
{
  "role": "assistant",
  "content": "",
  "finish_reason": "incomplete",
  "codex_reasoning_items": [
    {"type": "reasoning", "encrypted_content": "..."}
  ]
}
  1. After repeated empty responses, Hermes activated fallback:
Fallback activated: gpt-5.5 → claude-opus-4-7 (anthropic)
  1. The Anthropic pre-call sanitizer dropped regular reasoning-only assistant turns, but did not classify the Codex-only shape above as thinking-only because it only checks reasoning, reasoning_content, and reasoning_details.
  2. The Anthropic message converter then converted empty assistant content to "(empty)" because Anthropic rejects empty assistant content.
  3. Two consecutive empty assistant turns were merged, producing an Anthropic messages body effectively shaped like:
[
  {"role": "user", "content": [{"type": "text", "text": "..."}]},
  {"role": "assistant", "content": [
    {"type": "text", "text": "(empty)"},
    {"type": "text", "text": "(empty)"}
  ]}
]
  1. Anthropic rejected the request because the conversation ended with assistant content, which is interpreted as assistant prefill.

Relevant code paths

  • agent/codex_responses_adapter.py: extracts Codex reasoning items with encrypted_content and stores them as codex_reasoning_items.
  • agent/chat_completion_helpers.py: _build_assistant_message() preserves codex_reasoning_items on the assistant message for multi-turn continuity.
  • run_agent.py: _is_thinking_only_assistant() detects empty assistant turns with reasoning, reasoning_content, or reasoning_details, but not codex_reasoning_items.
  • agent/agent_runtime_helpers.py: drop_thinking_only_and_merge_users() therefore does not drop these Codex reasoning-only empty assistant turns.
  • agent/anthropic_adapter.py: convert_messages_to_anthropic() converts empty assistant content into "(empty)", then role alternation merging can leave the request ending in assistant content.
  • agent/agent_runtime_helpers.py: dump_api_request_debug() currently renders the debug URL as /chat/completions for every non-codex_responses API mode, which is misleading for anthropic_messages failures.

Expected behavior

When falling back from Codex Responses to Anthropic:

  • Codex-only empty reasoning state should not become visible assistant history for Anthropic.
  • The final Anthropic messages list should not end with an assistant turn unless the target model explicitly supports assistant prefill.
  • Debug dumps should identify anthropic_messages as /v1/messages or otherwise make clear that the URL is synthetic.

Suggested fixes

  1. Treat codex_reasoning_items as a reasoning-only payload in _is_thinking_only_assistant() when content is empty and there are no tool calls.
  2. Add a fallback/provider-switch invariant before Anthropic calls: do not send a final assistant-only scaffold to Anthropic.
  3. Consider pruning internal empty/prefill scaffolding when crossing provider families, especially from Codex Responses to Anthropic Messages.
  4. Update dump_api_request_debug() URL rendering for anthropic_messages so debugging does not incorrectly suggest /chat/completions was used.

Why this matters

This makes Anthropic fallback look broken even though native Anthropic support and credentials are otherwise valid. It also makes debugging harder because the synthetic request dump URL currently points to /chat/completions, while the body shape is Anthropic Messages API style.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

FAQ

Expected behavior

When falling back from Codex Responses to Anthropic:

  • Codex-only empty reasoning state should not become visible assistant history for Anthropic.
  • The final Anthropic messages list should not end with an assistant turn unless the target model explicitly supports assistant prefill.
  • Debug dumps should identify anthropic_messages as /v1/messages or otherwise make clear that the URL is synthetic.

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING