Skip to content

[Bug] TUI sessions do not propagate fallback_model/fallback_providers to AIAgent #28753

@x-hansong

Description

@x-hansong

Bug

React/Ink TUI sessions do not receive the configured fallback_model / fallback_providers chain when tui_gateway.server._create_agent() constructs the foreground AIAgent. As a result, when the primary model fails (timeouts, connection errors, rate limits, etc.), the agent exhausts retries on the primary and returns API call failed after 3 retries instead of activating the configured fallback.

This is inconsistent with gateway/API sessions, which pass fallback_model=self._fallback_model into AIAgent and do activate the fallback chain.

Reproduction

  1. Configure a primary model plus fallback in ~/.hermes/config.yaml, for example:

    model:
      provider: openai-codex
      default: gpt-5.5
    
    fallback_model:
      provider: zenmux-anthropic
      model: anthropic/claude-sonnet-4.6

    (fallback_providers: list format should be affected the same way.)

  2. Start Hermes via the React/Ink TUI path (hermes --tui) so the session is served by tui_gateway.

  3. Make the primary provider fail repeatedly (observed with openai-codex/gpt-5.5 timeouts / connection errors).

  4. Observe that the turn fails after primary retries, with logs like:

    API call failed (attempt 3/3) ... provider=openai-codex ... model=gpt-5.5 summary=Request timed out.
    API call failed after 3 retries. Request timed out. | provider=openai-codex model=gpt-5.5
    
  5. No Fallback activated: gpt-5.5 → ... log appears for the TUI session.

Expected behavior

TUI sessions should respect the same global fallback configuration as CLI/gateway/API sessions. After retries are exhausted (or on eager-fallback errors), _try_activate_fallback() should see a non-empty _fallback_chain and switch to the configured fallback provider/model.

Actual behavior

The TUI-created agent has an empty fallback chain, so _try_activate_fallback() returns False immediately and the turn fails.

Root cause

tui_gateway/server.py::_create_agent() constructs AIAgent(...) without passing fallback_model:

return AIAgent(
    model=model,
    max_iterations=_cfg_max_turns(cfg, 90),
    provider=runtime.get("provider"),
    base_url=runtime.get("base_url"),
    api_key=runtime.get("api_key"),
    api_mode=runtime.get("api_mode"),
    ...
    session_db=_get_db(),
    ...
)

Because agent/agent_init.py only initializes _fallback_chain from the fallback_model constructor parameter, this path ends up with:

agent._fallback_chain = []

For comparison, gateway/run.py loads and passes the chain:

self._fallback_model = self._load_fallback_model()
...
AIAgent(..., fallback_model=self._fallback_model)

Suggested fix

Add a TUI equivalent of the gateway fallback loader and pass it into all relevant TUI-created AIAgent instances.

Minimal foreground-session fix:

def _load_fallback_model() -> list | dict | None:
    cfg = _load_config()
    return cfg.get("fallback_providers") or cfg.get("fallback_model") or None

# in _create_agent()
return AIAgent(
    ...
    fallback_model=_load_fallback_model(),
    ...
)

Also check _background_agent_kwargs() / background TUI runs: it currently forwards getattr(agent, "_fallback_model", None), which is legacy single-entry state. It may need to preserve the full fallback chain, e.g. getattr(agent, "_fallback_chain", None) or getattr(agent, "_fallback_model", None).

Related issues

This is the same class of bug as other entrypoints not propagating fallback config, but I could not find an existing TUI-specific issue:

Environment

  • Hermes Agent: current main/local install as of 2026-05-19
  • UI path: React/Ink TUI (tui_gateway.entry / tui_gateway.server)
  • Primary observed: openai-codex / gpt-5.5
  • Fallback observed/configured: custom Anthropic-compatible provider (zenmux-anthropic / anthropic/claude-sonnet-4.6)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/tuiTerminal UI (ui-tui/ + tui_gateway/)type/bugSomething isn't working

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions