From deaf6015d9b74d1ba9ad193f9e0387b7ac148383 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 15 Feb 2026 16:21:26 +0000 Subject: [PATCH] Move exception tracebacks to debug level to improve error visibility Tracebacks from LLM API errors (e.g. litellm) were printed to console via logging.error(exc_info=True), often spanning 50+ lines with chained exceptions. This buried useful info (env var hints, warnings) above the traceback where users couldn't see it. Now tracebacks are logged at debug level (visible with -v flag) and only the clean error message is shown by default. A tip about -v is shown in interactive mode. https://claude.ai/code/session_01W597xJtLq8XHce8i6F9LKg Signed-off-by: Claude --- holmes/core/tool_calling_llm.py | 13 +++++++------ holmes/core/tools.py | 2 +- holmes/interactive.py | 8 +++++--- holmes/main.py | 18 ++++++++++++------ holmes/utils/stream.py | 6 ++++-- 5 files changed, 29 insertions(+), 18 deletions(-) diff --git a/holmes/core/tool_calling_llm.py b/holmes/core/tool_calling_llm.py index c2e4507734..50bd706be8 100644 --- a/holmes/core/tool_calling_llm.py +++ b/holmes/core/tool_calling_llm.py @@ -491,19 +491,19 @@ def call( # type: ignore ): raise Exception( "The Azure model you chose is not supported. Model version 1106 and higher required." - ) + ) from e else: logging.error( f"LLM BadRequestError on model={self.llm.model} (iteration {i}): {e}", - exc_info=True, ) + logging.debug("Full traceback for BadRequestError:", exc_info=True) raise except Exception as e: logging.error( f"LLM call failed on model={self.llm.model} (iteration {i}): " f"{type(e).__name__}: {e}", - exc_info=True, ) + logging.debug("Full traceback for LLM call failure:", exc_info=True) raise response = full_response.choices[0] # type: ignore @@ -680,8 +680,9 @@ def _directly_invoke_tool_call( except Exception as e: logging.error( - f"Tool call to {tool_name} failed with an Exception", exc_info=True + f"Tool call to {tool_name} failed with an Exception: {e}" ) + logging.debug("Full traceback:", exc_info=True) tool_response = StructuredToolResult( status=StructuredToolResultStatus.ERROR, error=f"Tool call failed: {e}", @@ -1014,15 +1015,15 @@ def call_stream( else: logging.error( f"LLM BadRequestError on model={self.llm.model} (streaming iteration {i}): {e}", - exc_info=True, ) + logging.debug("Full traceback for BadRequestError:", exc_info=True) raise except Exception as e: logging.error( f"LLM call failed on model={self.llm.model} (streaming iteration {i}): " f"{type(e).__name__}: {e}", - exc_info=True, ) + logging.debug("Full traceback for LLM call failure:", exc_info=True) raise response_message = full_response.choices[0].message # type: ignore diff --git a/holmes/core/tools.py b/holmes/core/tools.py index 5925eb7124..b79f658097 100644 --- a/holmes/core/tools.py +++ b/holmes/core/tools.py @@ -575,8 +575,8 @@ def __execute_subprocess(self, cmd) -> Tuple[str, int]: except Exception as e: logger.error( f"An unexpected error occurred while running '{cmd}': {e}", - exc_info=True, ) + logger.debug("Full traceback:", exc_info=True) output = f"Command execution failed with error: {e}" return output, 1 diff --git a/holmes/interactive.py b/holmes/interactive.py index 8b428e6806..8983ac985a 100644 --- a/holmes/interactive.py +++ b/holmes/interactive.py @@ -1077,7 +1077,8 @@ def save_conversation_to_file( f"[bold {STATUS_COLOR}]Conversation saved to {json_output_file}[/bold {STATUS_COLOR}]" ) except Exception as e: - logging.error(f"Failed to save conversation: {e}", exc_info=e) + logging.error(f"Failed to save conversation: {e}") + logging.debug("Full traceback:", exc_info=e) console.print( f"[bold {ERROR_COLOR}]Failed to save conversation: {e}[/bold {ERROR_COLOR}]" ) @@ -1432,8 +1433,9 @@ def get_bottom_toolbar(): ) break except Exception as e: - logging.error("An error occurred during interactive mode:", exc_info=e) - console.print(f"[bold {ERROR_COLOR}]Error: {e}[/bold {ERROR_COLOR}]") + logging.debug("Full traceback for interactive mode error:", exc_info=e) + console.print(f"\n[bold {ERROR_COLOR}]Error: {e}[/bold {ERROR_COLOR}]") + console.print(f"[dim]Tip: Use -v for more details[/dim]") finally: # Print trace URL for debugging (works for both success and error cases) trace_url = tracer.get_trace_url() diff --git a/holmes/main.py b/holmes/main.py index f1bc048c02..bba2136160 100644 --- a/holmes/main.py +++ b/holmes/main.py @@ -472,7 +472,8 @@ def alertmanager( try: issues = source.fetch_issues() except Exception as e: - logging.error("Failed to fetch issues from alertmanager", exc_info=e) + logging.error(f"Failed to fetch issues from alertmanager: {e}") + logging.debug("Full traceback:", exc_info=e) return if alertmanager_limit is not None: @@ -596,7 +597,8 @@ def jira( try: issues = source.fetch_issues() except Exception as e: - logging.error("Failed to fetch issues from Jira", exc_info=e) + logging.error(f"Failed to fetch issues from Jira: {e}") + logging.debug("Full traceback:", exc_info=e) return console.print( @@ -688,7 +690,8 @@ def ticket( if issue_to_investigate is None: raise Exception(f"Issue {ticket_id} Not found") except Exception as e: - logging.error(f"Failed to fetch issue from {source}", exc_info=e) + logging.error(f"Failed to fetch issue from {source}: {e}") + logging.debug("Full traceback:", exc_info=e) console.print( f"[bold red]Error: Failed to fetch issue {ticket_id} from {source}.[/bold red]" ) @@ -792,7 +795,8 @@ def github( try: issues = source.fetch_issues() except Exception as e: - logging.error("Failed to fetch issues from GitHub", exc_info=e) + logging.error(f"Failed to fetch issues from GitHub: {e}") + logging.debug("Full traceback:", exc_info=e) return console.print( @@ -873,7 +877,8 @@ def pagerduty( try: issues = source.fetch_issues() except Exception as e: - logging.error("Failed to fetch issues from PagerDuty", exc_info=e) + logging.error(f"Failed to fetch issues from PagerDuty: {e}") + logging.debug("Full traceback:", exc_info=e) return console.print( @@ -956,7 +961,8 @@ def opsgenie( try: issues = source.fetch_issues() except Exception as e: - logging.error("Failed to fetch issues from OpsGenie", exc_info=e) + logging.error(f"Failed to fetch issues from OpsGenie: {e}") + logging.debug("Full traceback:", exc_info=e) return console.print( diff --git a/holmes/utils/stream.py b/holmes/utils/stream.py index 2956f781bd..8765eeefd0 100644 --- a/holmes/utils/stream.py +++ b/holmes/utils/stream.py @@ -91,7 +91,8 @@ def stream_investigate_formatter( else: yield create_sse_message(message.event.value, message.data) except Exception as e: - logging.error(f"Error during streaming investigation: {e}", exc_info=True) + logging.error(f"Error during streaming investigation: {e}") + logging.debug("Full traceback:", exc_info=True) if _is_rate_limit_error(e): yield create_rate_limit_error_message(str(e)) else: @@ -131,7 +132,8 @@ def stream_chat_formatter( else: yield create_sse_message(message.event.value, message.data) except Exception as e: - logging.error(f"Error during streaming chat: {e}", exc_info=True) + logging.error(f"Error during streaming chat: {e}") + logging.debug("Full traceback:", exc_info=True) if _is_rate_limit_error(e): yield create_rate_limit_error_message(str(e)) else: