mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
fix(agent): guard response.text access in _summarize_api_error against httpx.ResponseNotRead
When an API error carries an httpx.Response whose body was consumed via iter_bytes() during streaming error handling (e.g. GeminiAPIError from agent/gemini_native_adapter.py), accessing .text raises httpx.ResponseNotRead. The secondary exception replaced the real, already-computed provider error (429 free-tier quota guidance) with the generic 'Attempted to access streaming response content' message on every turn. Guard the .text access so it degrades to an empty snippet and falls through to the str(error) fallback, which carries the full original message. Mirrors the existing guards in agent/error_classifier.py::_extract_error_body() and agent/gemini_native_adapter.py::gemini_http_error(). Fixes #59769 Salvaged from PR #59868 (guard + regression test); the unrelated desktop Ctrl-C fix bundled in that PR was intentionally dropped and is triaged separately.
This commit is contained in:
parent
31e39dec84
commit
0569a637d0
2 changed files with 30 additions and 1 deletions
|
|
@ -2143,7 +2143,10 @@ class AIAgent:
|
|||
# widens exposure vs the old empty-body "HTTP 400" string).
|
||||
response = getattr(error, "response", None)
|
||||
if response is not None:
|
||||
snippet = (getattr(response, "text", None) or "").strip()
|
||||
try:
|
||||
snippet = (getattr(response, "text", None) or "").strip()
|
||||
except Exception:
|
||||
snippet = ""
|
||||
if snippet:
|
||||
status_code = getattr(error, "status_code", None)
|
||||
prefix = f"HTTP {status_code}: " if status_code else ""
|
||||
|
|
|
|||
|
|
@ -12,6 +12,10 @@ provider error. This is a diagnostic improvement and is platform-agnostic.
|
|||
|
||||
from types import SimpleNamespace
|
||||
|
||||
from typing import Any
|
||||
|
||||
import httpx
|
||||
|
||||
from run_agent import AIAgent
|
||||
|
||||
|
||||
|
|
@ -54,3 +58,25 @@ def test_empty_body_fallback_redacts_secrets(monkeypatch):
|
|||
summary = AIAgent._summarize_api_error(err)
|
||||
assert "sk-proj-ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdef" not in summary
|
||||
|
||||
|
||||
def test_unread_streaming_response_does_not_crash_and_falls_back_to_exception_message():
|
||||
"""Unread streaming responses must not replace the real provider error."""
|
||||
|
||||
class _StreamingError(Exception):
|
||||
def __init__(self):
|
||||
super().__init__("Gemini HTTP 429: quota exceeded")
|
||||
self.status_code = 429
|
||||
self.response: Any = None
|
||||
|
||||
err = _StreamingError()
|
||||
|
||||
class _UnreadStreamingResponse:
|
||||
@property
|
||||
def text(self):
|
||||
raise httpx.ResponseNotRead()
|
||||
|
||||
err.response = _UnreadStreamingResponse()
|
||||
summary = AIAgent._summarize_api_error(err)
|
||||
assert "HTTP 429" in summary
|
||||
assert "Gemini HTTP 429: quota exceeded" in summary
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue