fix: trigger model fallback on transport errors (connection/timeout/overloaded) - #50228
Closed
linyubin wants to merge 1 commit into
Closed
fix: trigger model fallback on transport errors (connection/timeout/overloaded)#50228linyubin wants to merge 1 commit into
linyubin wants to merge 1 commit into
Conversation
…verloaded) Previously, the fallback chain only activated for rate-limit (429) and billing (402) errors. When the primary model's API was unreachable (connection error, timeout, provider overloaded), the agent retried the same failing provider until max_iterations was exhausted, with no attempt to switch to a configured fallback provider. Changes: - error_classifier.py: Mark transport errors (ConnectionError, ConnectError, APIConnectionError, timeout, overloaded) with should_fallback=True so the conversation loop can use this signal. - conversation_loop.py: Expand the fallback trigger condition to include FailoverReason.timeout and FailoverReason.overloaded. Transport errors allow 1 retry first (for transient hiccups) before falling back, while rate-limit/billing keep their eager behavior. This ensures cron jobs and interactive sessions automatically recover when the primary model provider is temporarily unreachable, switching to the configured fallback (e.g. deepseek) instead of failing.
teknium1
added a commit
that referenced
this pull request
Jun 28, 2026
Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR #50228 by @linyubin. Closes #22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
teknium1
added a commit
that referenced
this pull request
Jun 28, 2026
teknium1
added a commit
that referenced
this pull request
Jun 28, 2026
Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR #50228 by @linyubin. Closes #22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
teknium1
added a commit
that referenced
this pull request
Jun 28, 2026
This was referenced Jun 28, 2026
Closed
pai-scaffolde
pushed a commit
to pai-scaffolde/hermes-agent
that referenced
this pull request
Jun 28, 2026
…esearch#22277) Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR NousResearch#50228 by @linyubin. Closes NousResearch#22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
pai-scaffolde
pushed a commit
to pai-scaffolde/hermes-agent
that referenced
this pull request
Jun 28, 2026
lenardhuebner88-rgb
pushed a commit
to lenardhuebner88-rgb/hermes-agent
that referenced
this pull request
Jun 28, 2026
…esearch#22277) Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR NousResearch#50228 by @linyubin. Closes NousResearch#22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com> (cherry picked from commit c946e67)
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
…esearch#22277) Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR NousResearch#50228 by @linyubin. Closes NousResearch#22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
waefrebeorn
pushed a commit
to waefrebeorn/slermes
that referenced
this pull request
Jul 2, 2026
Jasper6439
pushed a commit
to Jasper6439/hermes-agent
that referenced
this pull request
Jul 5, 2026
…esearch#22277) Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR NousResearch#50228 by @linyubin. Closes NousResearch#22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
Jasper6439
pushed a commit
to Jasper6439/hermes-agent
that referenced
this pull request
Jul 5, 2026
habarmc1223-sudo
pushed a commit
to habarmc1223-sudo/hermes-agent-fluxmem
that referenced
this pull request
Jul 8, 2026
…esearch#22277) Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR NousResearch#50228 by @linyubin. Closes NousResearch#22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
habarmc1223-sudo
pushed a commit
to habarmc1223-sudo/hermes-agent-fluxmem
that referenced
this pull request
Jul 8, 2026
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
…esearch#22277) Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR NousResearch#50228 by @linyubin. Closes NousResearch#22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
santhreal
pushed a commit
to santhreal/hermes-agent
that referenced
this pull request
Jul 13, 2026
Contributor
|
Thanks for the transport-fallback fix. This is an automated hermes-sweeper review: the behavioral change is already implemented on current
Closing as already implemented on main, with contributor attribution preserved in the salvage commit. |
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
…esearch#22277) Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR NousResearch#50228 by @linyubin. Closes NousResearch#22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
Gravezzz
pushed a commit
to Gravezzz/hermes-agent
that referenced
this pull request
Jul 21, 2026
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
…esearch#22277) Eager fallback previously fired only on rate_limit/billing. A stale- detector-killed hung stream classifies as FailoverReason.timeout (retryable=True) and the retry loop re-hit the same dead primary until the budget exhausted -- 3 x ~180-300s stale kills compounding into a 15+ min silent hang while the configured fallback chain sat idle. Extend the existing eager-fallback gate to also cover timeout and overloaded, but only after one real retry (retry_count >= 2) so genuine transient hiccups still recover on the primary. Reuses the same pool-recovery guard and state-reset as the rate_limit branch -- no new config flag, no change to the rate-limit intent. Salvaged from PR NousResearch#50228 by @linyubin. Closes NousResearch#22277. Co-authored-by: Hermes Agent <127238744+teknium1@users.noreply.github.com>
leewenjie
pushed a commit
to leewenjie/hermes-agent
that referenced
this pull request
Aug 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Previously, the fallback chain only activated for rate-limit (429) and billing (402) errors. When the primary model's API was unreachable (connection error, timeout, provider overloaded), the agent retried the same failing provider until max_iterations was exhausted, with no attempt to switch to a configured fallback provider.
This was observed in production: a cron job using mimo-v2.5 failed repeatedly with
RuntimeError: Connection error.even thoughfallback_providers: [deepseek]was configured. The fallback never triggered because connection errors weren't in the fallback activation set.Changes
agent/error_classifier.pyMark transport errors (
ConnectionError,ConnectError,APIConnectionError, timeout, overloaded) withshould_fallback=Trueso the conversation loop can use this signal.agent/conversation_loop.pyExpand the fallback trigger condition to include
FailoverReason.timeoutandFailoverReason.overloaded:Testing
test_error_classifier.py: 161/161 ✅test_stream_drop_logging.py: 10/10 ✅test_auxiliary_client.py: 222/222 ✅test_scheduler.py: 135/136 (1 pre-existing failure unrelated to this change)Impact
This fix benefits both cron jobs and interactive sessions. When the primary model provider is temporarily unreachable, the agent automatically recovers by switching to the configured fallback instead of failing after exhausting retries.