From d560f2d1f28b5332397a470529e67f90a8601135 Mon Sep 17 00:00:00 2001 From: Test Date: Fri, 20 Mar 2026 21:06:55 -0700 Subject: [PATCH] fix(display): show provider and endpoint in API error messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an API call fails, the error output now shows the provider name, model, and endpoint URL so users can immediately identify which service rejected their request. Auth errors (401/403) get actionable guidance: check key validity, model access, and OpenRouter credits link. Before: 'API call failed (attempt 1/3): PermissionDeniedError' After: 'API call failed (attempt 1/3): PermissionDeniedError Provider: openrouter Model: anthropic/claude-sonnet-4 Endpoint: https://openrouter.ai/api/v1 Your API key was rejected by the provider. Check: • Is the key valid? Run: hermes setup • Does your account have access to anthropic/claude-sonnet-4? • Check credits: https://openrouter.ai/settings/credits' --- run_agent.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/run_agent.py b/run_agent.py index 1c3b25fe22..0e01206a6f 100644 --- a/run_agent.py +++ b/run_agent.py @@ -5982,10 +5982,14 @@ class AIAgent: api_error, ) + _provider = getattr(self, "provider", "unknown") + _base = getattr(self, "base_url", "unknown") + _model = getattr(self, "model", "unknown") self._vprint(f"{self.log_prefix}⚠️ API call failed (attempt {retry_count}/{max_retries}): {error_type}", force=True) - self._vprint(f"{self.log_prefix} ⏱️ Time elapsed before failure: {elapsed_time:.2f}s") + self._vprint(f"{self.log_prefix} 🔌 Provider: {_provider} Model: {_model}", force=True) + self._vprint(f"{self.log_prefix} 🌐 Endpoint: {_base}", force=True) self._vprint(f"{self.log_prefix} 📝 Error: {str(api_error)[:200]}", force=True) - self._vprint(f"{self.log_prefix} 📊 Request context: {len(api_messages)} messages, ~{approx_tokens:,} tokens, {len(self.tools) if self.tools else 0} tools") + self._vprint(f"{self.log_prefix} ⏱️ Elapsed: {elapsed_time:.2f}s Context: {len(api_messages)} msgs, ~{approx_tokens:,} tokens") # Check for interrupt before deciding to retry if self._interrupt_requested: @@ -6195,8 +6199,18 @@ class AIAgent: self._dump_api_request_debug( api_kwargs, reason="non_retryable_client_error", error=api_error, ) - self._vprint(f"{self.log_prefix}❌ Non-retryable client error detected. Aborting immediately.", force=True) - self._vprint(f"{self.log_prefix} 💡 This type of error won't be fixed by retrying.", force=True) + self._vprint(f"{self.log_prefix}❌ Non-retryable client error (HTTP {status_code}). Aborting.", force=True) + self._vprint(f"{self.log_prefix} 🔌 Provider: {_provider} Model: {_model}", force=True) + self._vprint(f"{self.log_prefix} 🌐 Endpoint: {_base}", force=True) + # Actionable guidance for common auth errors + if status_code in (401, 403) or "unauthorized" in error_msg or "forbidden" in error_msg or "permission" in error_msg: + self._vprint(f"{self.log_prefix} 💡 Your API key was rejected by the provider. Check:", force=True) + self._vprint(f"{self.log_prefix} • Is the key valid? Run: hermes setup", force=True) + self._vprint(f"{self.log_prefix} • Does your account have access to {_model}?", force=True) + if "openrouter" in str(_base).lower(): + self._vprint(f"{self.log_prefix} • Check credits: https://openrouter.ai/settings/credits", force=True) + else: + self._vprint(f"{self.log_prefix} 💡 This type of error won't be fixed by retrying.", force=True) logging.error(f"{self.log_prefix}Non-retryable client error: {api_error}") # Skip session persistence when the error is likely # context-overflow related (status 400 + large session).