{"record":{"id":"c69be86f2fe1dfe1","repo":"datawhalechina/hello-agents","slug":"str-e-c69be8","errorCode":null,"errorMessage":"流式工具调用失败: {str(e)}","messagePattern":"流式工具调用失败: (.+?)","errorType":"exception","errorClass":"HelloAgentsException","httpStatus":null,"severity":"error","filePath":"Co-creation-projects/tino-chen-HelloClaw/src/agent/enhanced_llm.py","lineNumber":236,"sourceCode":"                        if tc_delta.function and tc_delta.function.arguments:\n                            args_delta = tc_delta.function.arguments\n                            result.add_tool_call_delta(idx, args_delta)\n                            yield StreamToolEvent(\n                                event_type=StreamToolEventType.TOOL_CALL_DELTA,\n                                tool_call_index=idx,\n                                tool_arguments_delta=args_delta\n                            )\n\n                # 处理结束原因\n                if choice.finish_reason:\n                    result.finish_reason = choice.finish_reason\n                    yield StreamToolEvent(\n                        event_type=StreamToolEventType.FINISH,\n                        finish_reason=choice.finish_reason\n                    )\n\n        except Exception as e:\n            raise HelloAgentsException(f\"流式工具调用失败: {str(e)}\")\n\n        # 保存累积结果供后续使用\n        self._last_stream_tool_result = result\n\n    def get_last_stream_tool_result(self) -> Optional[StreamToolCallResult]:\n        \"\"\"\n        获取最后一次流式工具调用的累积结果\n\n        Returns:\n            StreamToolCallResult 或 None\n        \"\"\"\n        return self._last_stream_tool_result\n","sourceCodeStart":218,"sourceCodeEnd":249,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/tino-chen-HelloClaw/src/agent/enhanced_llm.py#L218-L249","documentation":"In enhanced_llm.py, the streaming tool-call loop wraps its entire body in try/except Exception and re-raises as HelloAgentsException('流式工具调用失败: ...'). The original exception text is preserved in the message, so the real cause is whatever the streaming SDK call raised: provider auth errors, rate limits, malformed tool-call deltas, network resets, or bugs in delta-handling code. Because the except swallows the type, callers cannot narrow on the original exception class.","triggerScenarios":"Invalid/expired API key when the stream request is opened; HTTP 429/5xx from the provider mid-stream; tool_calls deltas arriving in an order the accumulation logic does not expect (index/id None); connection reset or read timeout while yielding chunks; an SDK version change in the stream chunk schema causing AttributeError inside the loop.","commonSituations":"Long streaming sessions hitting provider timeouts; switching LLM providers/base_url without updating SDK assumptions; concurrent streams exceeding rate limits; hello-agents/OpenAI SDK version mismatch after upgrade.","solutions":["Read the wrapped text — it contains the provider/SDK error verbatim; fix that first (key, quota, model name, base_url).","Retry with exponential backoff for transient 429/5xx/network-reset causes; make the retry re-open the stream, not resume it.","Pin compatible SDK versions (openai/hello-agents) if the message shows AttributeError/KeyError on chunk fields.","Increase the client read timeout for long tool-call generations.","Change the wrapper to `raise HelloAgentsException(...) from e` so the traceback retains the original exception for diagnosis."],"exampleFix":"# before\nexcept Exception as e:\n    raise HelloAgentsException(f\"流式工具调用失败: {str(e)}\")\n# after\nexcept Exception as e:\n    raise HelloAgentsException(f\"流式工具调用失败: {str(e)}\") from e  # keeps original traceback","handlingStrategy":"retry","validationCode":"import os\nrequired = [\"LLM_API_KEY\"]  # adjust to this deployment's env names\nmissing = [k for k in required if not os.getenv(k)]\nif missing:\n    raise SystemExit(f\"missing env: {missing}\")","typeGuard":null,"tryCatchPattern":"from hello_agents import HelloAgentsException\nimport time\n\nfor attempt in range(3):\n    try:\n        result = consume_stream(llm.stream_tool_call(...))\n        break\n    except HelloAgentsException as e:\n        msg = str(e)\n        if any(t in msg for t in (\"429\", \"rate\", \"timeout\", \"reset\")) and attempt < 2:\n            time.sleep(2 ** attempt)  # transient — back off and re-open the stream\n            continue\n        raise  # auth/validation errors are not retryable","preventionTips":["Retry only transient causes (429/5xx/reset) and always re-open the stream","Set generous read timeouts for long tool-call streams","Upgrade hello-agents and the provider SDK together, pinned in requirements","Log the wrapped message verbatim — it contains the real provider error"],"tags":["streaming","llm-failure","exception-wrapping","python","network"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}