From 9eccb11edf692c2f701fda590d7a5e1dca4f7c09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Angelantoni?= Date: Wed, 15 Apr 2026 12:30:35 -0700 Subject: [PATCH] fix(agent): notify gateway users when all provider credentials are auth-exhausted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When all credentials for a provider are exhausted due to 401/403 failures, emit a plain-language _emit_status() notification so gateway users (Telegram, Discord, etc.) know their primary AI has become unavailable and what to do. Same-provider key rotation remains silent — the message only fires when rotation itself fails and Hermes is forced to fall back. This is distinct from the cooldown duration change in PR #10058 (which was closed). The notification half of that fix stands on its own: the configured fallback_model path already calls _emit_status() on provider switch, so this makes the credential pool exhaustion path consistent with that behavior. Closes #10476 --- run_agent.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/run_agent.py b/run_agent.py index d7d1249be9..c45c3cfaac 100644 --- a/run_agent.py +++ b/run_agent.py @@ -4799,6 +4799,18 @@ class AIAgent: ) self._swap_credential(next_entry) return True, False + # All credentials for this provider are exhausted due to an auth + # failure. Emit a plain-language notification so gateway users + # (Telegram, Discord, etc.) know their primary AI is unavailable + # and what to do about it. Same-provider key rotation (above) + # remains silent — the message only fires when rotation fails. + _provider_label = getattr(self, "provider", "unknown") + self._emit_status( + f"⚠️ Primary AI ({_provider_label}) is unavailable — the API key " + f"may be invalid or expired (HTTP {rotate_status}). Switched to " + f"fallback if one is configured. To restore: check your API key " + f"and run `hermes auth reset {_provider_label}`." + ) return False, has_retried_429