hermes - 💡(How to fix) Fix Bug: successful fallback activation is silent because buffered notices are never flushed [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…

Recent Hermes versions can activate a fallback provider without sending any user-visible fallback notice on messaging gateways. The logs show Fallback activated: primary → fallback, but Discord/Telegram users only see the final answer from the fallback model, making the model/provider switch silent.

This is a regression from older behavior documented in existing issues where users did see messages like:

⚠️ Empty/malformed response — switching to fallback...
🔄 Primary model failed — switching to fallback: <model> via <provider>

See related UX/config issues: #19572, #27131, #19002, #33913.

Error Message

When the primary provider returns an empty/malformed response and the fallback succeeds:

Root Cause

This is not just cosmetic. For users with strict provider trust/cost/quality expectations, silent fallback changes:

  • provider and data boundary
  • model quality
  • context/compression thresholds
  • pricing/accounting
  • streaming behavior

A user may think they are receiving an answer from the configured primary model while logs/state show the answer actually came from the fallback provider.

Fix Action

Fixed

Code Example

⚠️ Empty/malformed response — switching to fallback...
🔄 Primary model failed — switching to fallback: <model> via <provider>

---

Fallback activated: claude-opus-4.8 → deepseek-ai/DeepSeek-V4-Pro (sf)

---

if response_invalid:
    if agent._fallback_index < len(agent._fallback_chain):
        agent._buffer_status("⚠️ Empty/malformed response — switching to fallback...")
    if agent._try_activate_fallback():
        retry_count = 0
        compression_attempts = 0
        primary_recovery_attempted = False
        continue

---

agent._buffer_status(
    f"🔄 Primary model failed — switching to fallback: "
    f"{fb_model} via {fb_provider}"
)
logger.info("Fallback activated: %s → %s (%s)", old_model, fb_model, fb_provider)
return True

---

🔄 Primary model failed; answering via fallback: deepseek-ai/DeepSeek-V4-Pro (sf)
RAW_BUFFERClick to expand / collapse

Summary

Recent Hermes versions can activate a fallback provider without sending any user-visible fallback notice on messaging gateways. The logs show Fallback activated: primary → fallback, but Discord/Telegram users only see the final answer from the fallback model, making the model/provider switch silent.

This is a regression from older behavior documented in existing issues where users did see messages like:

⚠️ Empty/malformed response — switching to fallback...
🔄 Primary model failed — switching to fallback: <model> via <provider>

See related UX/config issues: #19572, #27131, #19002, #33913.

Observed behavior

When the primary provider returns an empty/malformed response and the fallback succeeds:

  • agent.log contains:
    Fallback activated: claude-opus-4.8 → deepseek-ai/DeepSeek-V4-Pro (sf)
  • The user receives only the final response from the fallback model.
  • No visible lifecycle/status message tells the user the primary model failed or that the answer came from a fallback provider.

This makes it impossible for a chat user to distinguish:

  1. primary model answered successfully
  2. primary model failed, fallback answered
  3. fallback changed context/compression limits and pricing/provider trust boundary

Concrete code path

In agent/conversation_loop.py, invalid responses trigger eager fallback:

if response_invalid:
    if agent._fallback_index < len(agent._fallback_chain):
        agent._buffer_status("⚠️ Empty/malformed response — switching to fallback...")
    if agent._try_activate_fallback():
        retry_count = 0
        compression_attempts = 0
        primary_recovery_attempted = False
        continue

In agent/chat_completion_helpers.py::try_activate_fallback, successful activation also buffers the user-facing notice:

agent._buffer_status(
    f"🔄 Primary model failed — switching to fallback: "
    f"{fb_model} via {fb_provider}"
)
logger.info("Fallback activated: %s → %s (%s)", old_model, fb_model, fb_provider)
return True

But because the fallback succeeds and the agent continues to produce a normal final answer, the buffered status messages are never flushed to the gateway status callback. The result is silent provider/model switching.

Why this matters

This is not just cosmetic. For users with strict provider trust/cost/quality expectations, silent fallback changes:

  • provider and data boundary
  • model quality
  • context/compression thresholds
  • pricing/accounting
  • streaming behavior

A user may think they are receiving an answer from the configured primary model while logs/state show the answer actually came from the fallback provider.

Expected behavior

One of these should happen by default:

  1. Emit a single concise fallback notice when fallback activation succeeds, e.g.
    🔄 Primary model failed; answering via fallback: deepseek-ai/DeepSeek-V4-Pro (sf)
  2. Or attach fallback metadata to the final response/status envelope so gateways can display it.
  3. If suppression is desired, make it explicit via a config knob (related to #19572 / #27131), not the accidental default caused by buffered statuses never flushing.

Reproduction outline

  1. Configure a primary provider that returns an empty/malformed response.
  2. Configure fallback_providers with a healthy fallback.
  3. Send a message through Discord/Telegram gateway.
  4. Observe:
    • logs show Fallback activated: ...
    • user receives final answer from fallback
    • no fallback notice is sent to the chat

Suggested fix

When _try_activate_fallback() succeeds, either:

  • call _emit_status(...) for the successful fallback activation notice instead of _buffer_status(...), or
  • flush buffered lifecycle statuses immediately before retrying with the fallback, or
  • record a per-turn fallback notice and include it with the final gateway response unless display.lifecycle_messages / fallback_notice.style explicitly suppresses it.

The important invariant: successful fallback must not silently change the answering provider unless the user explicitly configured fallback notices to be silent.

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

One of these should happen by default:

  1. Emit a single concise fallback notice when fallback activation succeeds, e.g.
    🔄 Primary model failed; answering via fallback: deepseek-ai/DeepSeek-V4-Pro (sf)
  2. Or attach fallback metadata to the final response/status envelope so gateways can display it.
  3. If suppression is desired, make it explicit via a config knob (related to #19572 / #27131), not the accidental default caused by buffered statuses never flushing.

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: successful fallback activation is silent because buffered notices are never flushed [1 pull requests]