From 4c2253dc76987a9b899ceb1638e7886b43b3639b Mon Sep 17 00:00:00 2001 From: cthomas Date: Thu, 22 Jan 2026 16:34:08 -0800 Subject: [PATCH] fix: use repr() fallback for empty exception messages in error logging (#9047) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Problem:** Error logs showed empty detail fields when exceptions had no message: ``` Error during step processing: Run run-xxx stopped with unknown error: , error_data: {...'detail': ''} ``` This made debugging production issues difficult as the actual error type was hidden. **Root Cause:** Python exceptions created with no arguments (e.g., `Exception()` or caught and re-raised in certain ways) have `str(e) == ""`: ```python e = Exception() str(e) # Returns "" repr(e) # Returns "Exception()" ``` When exceptions with empty string representations were caught, all logging and error messages showed blank details. **Fix:** Use `str(e) or repr(e)` fallback pattern in 3 places: 1. `letta_agent_v3.py` stream() exception handler (line 406) 2. `letta_agent_v3.py` step() exception handler (line 928) 3. `streaming_service.py` generic exception handler (line 469) **Result:** - Error logs now show `Exception()` or similar instead of empty string - Helps identify exception types even when message is missing - Better production debugging without changing exception handling logic 👾 Generated with [Letta Code](https://letta.com) Co-authored-by: Letta --- letta/agents/letta_agent_v3.py | 12 ++++++++---- letta/services/streaming_service.py | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/letta/agents/letta_agent_v3.py b/letta/agents/letta_agent_v3.py index b0df650c..098e21b0 100644 --- a/letta/agents/letta_agent_v3.py +++ b/letta/agents/letta_agent_v3.py @@ -402,7 +402,9 @@ class LettaAgentV3(LettaAgentV2): self.stop_reason = LettaStopReason(stop_reason=StopReasonType.end_turn.value) except Exception as e: - self.logger.warning(f"Error during agent stream: {e}", exc_info=True) + # Use repr() if str() is empty (happens with Exception() with no args) + error_detail = str(e) or repr(e) + self.logger.warning(f"Error during agent stream: {error_detail}", exc_info=True) # Set stop_reason if not already set if self.stop_reason is None: @@ -423,7 +425,7 @@ class LettaAgentV3(LettaAgentV2): run_id=run_id, error_type="internal_error", message="An error occurred during agent execution.", - detail=str(e), + detail=error_detail, ) yield f"event: error\ndata: {error_message.model_dump_json()}\n\n" @@ -923,8 +925,10 @@ class LettaAgentV3(LettaAgentV2): except Exception as e: # NOTE: message persistence does not happen in the case of an exception (rollback to previous state) - self.logger.warning(f"Error during step processing: {e}") - self.job_update_metadata = {"error": str(e)} + # Use repr() if str() is empty (happens with Exception() with no args) + error_detail = str(e) or repr(e) + self.logger.warning(f"Error during step processing: {error_detail}") + self.job_update_metadata = {"error": error_detail} # This indicates we failed after we decided to stop stepping, which indicates a bug with our flow. if not self.stop_reason: diff --git a/letta/services/streaming_service.py b/letta/services/streaming_service.py index 354e4490..563e1846 100644 --- a/letta/services/streaming_service.py +++ b/letta/services/streaming_service.py @@ -466,14 +466,16 @@ class StreamingService: except Exception as e: run_status = RunStatus.failed stop_reason = LettaStopReason(stop_reason=StopReasonType.error) + # Use repr() if str() is empty (happens with Exception() with no args) + error_detail = str(e) or repr(e) error_message = LettaErrorMessage( run_id=run_id, error_type="internal_error", message="An unknown error occurred with the LLM streaming request.", - detail=str(e), + detail=error_detail, ) error_data = {"error": error_message.model_dump()} - logger.error(f"Run {run_id} stopped with unknown error: {e}, error_data: {error_message.model_dump()}") + logger.error(f"Run {run_id} stopped with unknown error: {error_detail}, error_data: {error_message.model_dump()}") yield f"data: {stop_reason.model_dump_json()}\n\n" yield f"event: error\ndata: {error_message.model_dump_json()}\n\n" # Send [DONE] marker to properly close the stream