fix(agent): chat() surfaces error instead of KeyError on no-final_response result - #34770
fix(agent): chat() surfaces error instead of KeyError on no-final_response result#34770OmarB97 wants to merge 2 commits into
Conversation
…ponse result
AIAgent.chat() did `return result["final_response"]`, but several error/failure
return paths in agent.conversation_loop.run_conversation (API error after max
retries, billing/credits exhaustion, policy/toolguard halt, interrupts) return a
result dict with {messages, completed, api_calls, error, failed} and NO
'final_response' key. On those paths chat() raised `KeyError: 'final_response'`
instead of surfacing the error, so callers saw a traceback rather than the
actual failure (e.g. an API error).
Return `result.get("final_response") or result.get("error") or ""` so the error
message is surfaced and the call degrades gracefully. Also guard the CLI summary
printer with `result.get('final_response')`.
Adds tests for the error path, the happy path, and the empty case.
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Verified the fix is correct and the bug is real. Confirmed: The
Tests cover all three branches correctly. The One minor note: the |
|
Thanks for verifying, @liuhao1024 — appreciate the independent confirmation of the error paths. The branch is conflict-free and ready for maintainer review whenever someone gets a chance. |
|
Thanks for the focused regression report and tests. The reported failure mode is already fixed on current
Because |
What does this PR do?
AIAgent.chat()raisedKeyError: 'final_response'wheneverrun_conversationreturned via one of its error/failure paths. Those paths (API error after max
retries, billing/credits exhaustion, policy/toolguard halt, interrupts) return a
result dict shaped like
{messages, completed, api_calls, error, failed}withno
final_responsekey, soreturn result["final_response"]crashed —masking the real error behind a
KeyErrortraceback.This makes
chat()surface the error message and degrade gracefully instead ofcrashing.
Type of Change
Changes Made
run_agent.py—AIAgent.chat()now returnsresult.get("final_response") or result.get("error") or ""instead ofresult["final_response"], so error/failure results surface theirerrorstring rather than raising
KeyError.run_agent.py— guard the CLI conversation-summary printer withresult.get('final_response')(same omitted-key hazard).tests/test_chat_final_response_resilience.py— regression tests covering theerror path (no
final_response→ returnserror), the happy path, and theempty case.
How to Test
pytest tests/test_chat_final_response_resilience.py→ 3 passed.chat()call whose underlying conversation hit anerror path (e.g. an API error after retries) raised
KeyError: 'final_response'atrun_agent.pyreturn result["final_response"].After, it returns the error string and the caller can handle/log it.
Notes
Surfaced in practice when a non-interactive
hermes -zoneshot run hit aprovider error path: the launcher recorded a
KeyError: 'final_response'traceback instead of the underlying error.