{"record":{"id":"510f82c61bbe3102","repo":"datawhalechina/hello-agents","slug":"tool-name-str-e","errorCode":null,"errorMessage":"工具 '{tool_name}' 执行失败: {str(e)}","messagePattern":"工具 '(.+?)' 执行失败: (.+?)","errorType":"exception","errorClass":"AgentException","httpStatus":500,"severity":"error","filePath":"Co-creation-projects/Apricity-InnocoreAI/agents/base.py","lineNumber":84,"sourceCode":"                result = await asyncio.wait_for(\n                    tool_func(tool_input), \n                    timeout=self.timeout\n                )\n            else:\n                result = await asyncio.wait_for(\n                    asyncio.to_thread(tool_func, tool_input),\n                    timeout=self.timeout\n                )\n            \n            self._add_to_history(f\"Tool {tool_name} called with input: {tool_input}\")\n            self._add_to_history(f\"Tool {tool_name} result: {result}\")\n            \n            return result\n            \n        except asyncio.TimeoutError:\n            raise TimeoutException(f\"工具 '{tool_name}' 执行超时\")\n        except Exception as e:\n            raise AgentException(f\"工具 '{tool_name}' 执行失败: {str(e)}\")\n    \n    async def think(self, prompt: str, context: Dict = None) -> str:\n        \"\"\"调用LLM进行思考\"\"\"\n        try:\n            # 构建完整的提示词\n            full_prompt = prompt\n            \n            # 添加上下文信息\n            if context:\n                context_str = json.dumps(context, ensure_ascii=False, indent=2)\n                full_prompt = f\"上下文信息:\\n{context_str}\\n\\n任务:\\n{prompt}\"\n            \n            # 添加历史记录\n            if self.history:\n                history_str = \"\\n\".join(self.history[-10:])  # 只保留最近10条\n                full_prompt += f\"\\n\\n历史记录:\\n{history_str}\"\n            \n            # 调用 HelloAgent LLM","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/Apricity-InnocoreAI/agents/base.py#L66-L102","documentation":"Generic AgentException wrapper from BaseAgent.call_tool: any exception escaping the tool body other than asyncio.TimeoutError (which is caught first as error 44) is re-raised with the tool name and the original message appended. This is a lossy re-wrap — it chains no __cause__, so tracebacks of the original failure are harder to read, and known error types (ExternalAPIException from hunter tools) surface through it.","triggerScenarios":"A registered tool raising KeyError on malformed input (e.g. tool_input missing keys), aiohttp.ClientError on connection reset, feedparser returning entries lacking attributes (AttributeError), or an ExternalAPIException from a non-200 HTTP status inside the tool — all emerge as \"工具 'X' 执行失败: <original>\".","commonSituations":"LLM emitting tool arguments that don't match the tool's expected schema; upstream APIs (arXiv/IEEE) changing response shape; missing optional-dependency imports inside tool modules; encoding errors parsing PDFs.","solutions":["Read the suffix after '执行失败:' — it carries the original exception text, which is the real diagnosis.","Fix the root cause in the tool (schema-validate tool_input, guard entry.get(...) instead of attribute access).","Re-raise with raise ... from e in call_tool to preserve the traceback chain for debugging.","Add targeted excepts above the generic one (ExternalAPIException, ValueError) to keep typed errors typed end-to-end.","Cover tools with unit tests passing minimal valid input to catch schema mismatches early."],"exampleFix":"# before\nexcept Exception as e:\n    raise AgentException(f\"工具 '{tool_name}' 执行失败: {str(e)}\")\n\n# after — preserve cause and re-raise already-typed exceptions untouched\nexcept (AgentException, ExternalAPIException):\n    raise\nexcept Exception as e:\n    raise AgentException(f\"工具 '{tool_name}' 执行失败: {e}\") from e","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    result = await agent.call_tool(name, tool_input)\nexcept AgentException as e:\n    # suffix after '执行失败:' is the root cause\n    root = str(e).split('执行失败:', 1)[-1].strip()\n    logger.error(\"tool %s failed: %s\", name, root, exc_info=True)\n    raise","preventionTips":["Schema-validate tool_input against the tool's expected arguments before calling.","Patch call_tool to use 'raise ... from e' so tracebacks survive wrapping.","Unit-test each tool with minimal valid input in CI."],"tags":["python","agent","error-wrapping","tool-registry"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}