hermes - 💡(How to fix) Fix bug: /model TUI picker silently routes Bedrock Claude models through wrong API (bedrock_converse instead of anthropic_messages)

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…

Error Message

| Silent failure | No error, no warning — the session looks like it switched successfully | | Misleading error surface | Failures surface as auth/network/format errors, not as a routing bug |

Root Cause

File: hermes_cli/runtime_provider.py, inside the if provider == "bedrock": block of resolve_runtime_provider()

The function signature already accepts target_model: Optional[str] = None — the parameter is correctly populated by switch_model() in model_switch.py (line 863: resolve_runtime_provider(requested=target_provider, target_model=new_model)).

However, the bedrock dual-path routing section completely ignores target_model and instead reads from the stale persisted config:

# BUGGY — reads config.yaml model.default, not the live target_model
_current_model = str(model_cfg.get("default") or "").strip()
if is_anthropic_bedrock_model(_current_model):
    # → anthropic_messages path (correct for Claude)
else:
    # → bedrock_converse path (correct for Nova, DeepSeek, Llama)

At startup, config.yaml already has the correct model, so model_cfg.get("default") is accurate and everything works.

During a picker-initiated switch, config.yaml still holds the old model. So if you switch from a non-Claude Bedrock model to eu.anthropic.claude-sonnet-4-6, _current_model is set to the old non-Claude model ID, is_anthropic_bedrock_model() returns False, and you land on bedrock_converse instead of anthropic_messages.

The same docstring on resolve_runtime_provider explicitly warns about this:

"Callers performing an explicit mid-session model switch should pass the new model here so api_mode is derived from the model they are switching TO, not the stale persisted default."

Yet the bedrock branch never reads target_model.


Fix Action

Fix

One line, in hermes_cli/runtime_provider.py:

- _current_model = str(model_cfg.get("default") or "").strip()
+ _current_model = str(target_model or model_cfg.get("default") or "").strip()

The or chain is fully backward-compatible: callers that don't pass target_model (e.g. _ensure_runtime_credentials in cli.py) still fall back to config.yaml as before.


Code Example

# Works correctly — starts with the right model from config.yaml
hermes --tui --model bedrock

# Broken path:
hermes --tui
# then type: /model
# then select: bedrock → eu.anthropic.claude-sonnet-4-6 (or any regional Claude model)
# → send any message → agent fails or responds incorrectly

---

# BUGGY — reads config.yaml model.default, not the live target_model
_current_model = str(model_cfg.get("default") or "").strip()
if is_anthropic_bedrock_model(_current_model):
    # → anthropic_messages path (correct for Claude)
else:
    # → bedrock_converse path (correct for Nova, DeepSeek, Llama)

---

- _current_model = str(model_cfg.get("default") or "").strip()
+ _current_model = str(target_model or model_cfg.get("default") or "").strip()
RAW_BUFFERClick to expand / collapse

🚨 Severity: High — Silent Failure, Wrong API Path, Full Model Unavailability

Selecting a Bedrock model via the TUI /model picker produces a completely silent failure: the session appears to switch models, but every subsequent message is routed through the wrong API path (bedrock_converse instead of anthropic_messages). For Anthropic Claude models on Bedrock, this breaks the entire session — the agent never responds correctly, or returns errors that look like auth/network issues rather than a routing bug.


Steps to Reproduce

# Works correctly — starts with the right model from config.yaml
hermes --tui --model bedrock

# Broken path:
hermes --tui
# then type: /model
# then select: bedrock → eu.anthropic.claude-sonnet-4-6 (or any regional Claude model)
# → send any message → agent fails or responds incorrectly

Expected Behavior

Selecting any *.anthropic.claude-* Bedrock model from the TUI picker should:

  • Route through api_mode = anthropic_messages (AnthropicBedrock SDK)
  • Enable full feature parity: prompt caching, thinking budgets, adaptive thinking

Actual Behavior

After a picker-initiated switch, api_mode = bedrock_converse is selected for all Bedrock models — including Anthropic Claude models — because the routing logic reads the stale on-disk config.yaml instead of the model the user just selected. The session silently degrades to the Converse API, which does not support Anthropic-native features and may reject Claude-specific message formats entirely.


Root Cause

File: hermes_cli/runtime_provider.py, inside the if provider == "bedrock": block of resolve_runtime_provider()

The function signature already accepts target_model: Optional[str] = None — the parameter is correctly populated by switch_model() in model_switch.py (line 863: resolve_runtime_provider(requested=target_provider, target_model=new_model)).

However, the bedrock dual-path routing section completely ignores target_model and instead reads from the stale persisted config:

# BUGGY — reads config.yaml model.default, not the live target_model
_current_model = str(model_cfg.get("default") or "").strip()
if is_anthropic_bedrock_model(_current_model):
    # → anthropic_messages path (correct for Claude)
else:
    # → bedrock_converse path (correct for Nova, DeepSeek, Llama)

At startup, config.yaml already has the correct model, so model_cfg.get("default") is accurate and everything works.

During a picker-initiated switch, config.yaml still holds the old model. So if you switch from a non-Claude Bedrock model to eu.anthropic.claude-sonnet-4-6, _current_model is set to the old non-Claude model ID, is_anthropic_bedrock_model() returns False, and you land on bedrock_converse instead of anthropic_messages.

The same docstring on resolve_runtime_provider explicitly warns about this:

"Callers performing an explicit mid-session model switch should pass the new model here so api_mode is derived from the model they are switching TO, not the stale persisted default."

Yet the bedrock branch never reads target_model.


Fix

One line, in hermes_cli/runtime_provider.py:

- _current_model = str(model_cfg.get("default") or "").strip()
+ _current_model = str(target_model or model_cfg.get("default") or "").strip()

The or chain is fully backward-compatible: callers that don't pass target_model (e.g. _ensure_runtime_credentials in cli.py) still fall back to config.yaml as before.


Why This Is High Severity

FactorDetail
Silent failureNo error, no warning — the session looks like it switched successfully
Complete feature lossPrompt caching, thinking budgets, adaptive thinking, and extended context are unavailable via bedrock_converse
Affects all regional prefixeseu., us., ap., jp., global. Anthropic Claude models all break
Only path affected is the live picker--model bedrock at startup works fine — makes it very hard to diagnose without source inspection
Misleading error surfaceFailures surface as auth/network/format errors, not as a routing bug

Affected Components

  • hermes_cli/runtime_provider.py — bug location
  • hermes_cli/model_switch.py — caller (correct, no change needed)
  • Bedrock provider with any *.anthropic.claude-* model selected via /model TUI picker

Environment

  • Reproducible with any Bedrock-configured Hermes Agent instance
  • Regional inference profile model IDs: eu.anthropic.claude-sonnet-4-6, us.anthropic.claude-3-5-haiku-20241022, etc.

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…

Still need to ship something?

×6

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

Back to top recommendations

TRENDING

hermes - 💡(How to fix) Fix bug: /model TUI picker silently routes Bedrock Claude models through wrong API (bedrock_converse instead of anthropic_messages)