{"record":{"id":"cb325f36985a694b","repo":"datawhalechina/hello-agents","slug":"stage-exc","errorCode":null,"errorMessage":"{stage}阶段执行失败：{exc}","messagePattern":"(.+?)阶段执行失败：(.+?)","errorType":"exception","errorClass":"WorkflowExecutionError","httpStatus":null,"severity":"error","filePath":"Co-creation-projects/zenith191-RequirementClarifierAgent/src/workflow.py","lineNumber":133,"sourceCode":"    @staticmethod\n    def _validate_requirement(requirement: str) -> str:\n        if not isinstance(requirement, str):\n            raise WorkflowExecutionError(\"需求必须是字符串\")\n        requirement = requirement.strip()\n        if not requirement:\n            raise WorkflowExecutionError(\"需求不能为空\")\n        if len(requirement) > MAX_REQUIREMENT_LENGTH:\n            raise WorkflowExecutionError(\n                f\"需求文本不能超过 {MAX_REQUIREMENT_LENGTH} 个字符\"\n            )\n        return requirement\n\n    @staticmethod\n    def _run_agent(stage: str, agent: AgentLike, prompt: str) -> str:\n        try:\n            response = agent.run(prompt)\n        except Exception as exc:\n            raise WorkflowExecutionError(f\"{stage}阶段执行失败：{exc}\") from exc\n        if not isinstance(response, str) or not response.strip():\n            raise WorkflowExecutionError(f\"{stage}阶段返回了空结果\")\n        return response.strip()\n\n    def _run_tool(\n        self, name: str, parameters: dict[str, object], stage: str\n    ) -> dict[str, object]:\n        \"\"\"通过官方 ToolRegistry 获取工具并解析其字符串协议。\"\"\"\n\n        tool = self.tool_registry.get_tool(name)\n        if tool is None:\n            raise WorkflowExecutionError(f\"{stage}失败：工具 {name} 未注册\")\n        try:\n            raw_result = tool.run(parameters)\n        except Exception as exc:\n            raise WorkflowExecutionError(f\"{stage}失败：工具执行异常：{exc}\") from exc\n        try:\n            payload = json.loads(raw_result)","sourceCodeStart":115,"sourceCodeEnd":151,"githubUrl":"https://github.com/datawhalechina/hello-agents/blob/606a07d341a47be773fab7f4b71177f53f96b2c3/Co-creation-projects/zenith191-RequirementClarifierAgent/src/workflow.py#L115-L151","documentation":"Raised by _run_agent (src/workflow.py:133) when agent.run(prompt) raises any exception; the original is chained with 'from exc' and the stage name (analyst/architect/reviewer/synthesizer) is prefixed. It is a wrapper error — the root cause is whatever the underlying HelloAgents LLM call failed with (network, auth, rate limit, malformed response).","triggerScenarios":"Any analyst/architect/reviewer/synthesizer stage calling agent.run() while the LLM API key is invalid, the endpoint is unreachable, the rate limit is hit, or the model response cannot be parsed by the agent framework.","commonSituations":"Expired or wrong API key; wrong LLM_BASE_URL pointing at a provider that 404s; model ID not available on the account; transient 429/503 during peak usage; proxy/firewall blocking egress.","solutions":["Read the text after '执行失败：' — it carries the underlying exception; fix that first (auth → key, DNS/conn → base_url, 429 → backoff).","Check the stage name to see which agent failed; a first-stage failure usually means config, a later one often means malformed responses.","Add retry with exponential backoff around run() for transient network/429 errors.","Verify LLM_MODEL_ID, LLM_API_KEY, LLM_BASE_URL with a minimal curl/python call to the provider."],"exampleFix":"# before\nresult = workflow.run(req)\n\n# after\nfor attempt in range(3):\n    try:\n        result = workflow.run(req)\n        break\n    except WorkflowExecutionError as e:\n        if attempt == 2 or '429' not in str(e):\n            raise\n        time.sleep(2 ** attempt)","handlingStrategy":"retry","validationCode":"# cheap preflight: prove the LLM endpoint+key work before a long workflow\nimport os, requests\n\nresp = requests.post(\n    os.environ[\"LLM_BASE_URL\"].rstrip(\"/\") + \"/chat/completions\",\n    headers={\"Authorization\": f\"Bearer {os.environ['LLM_API_KEY']}\"},\n    json={\"model\": os.environ[\"LLM_MODEL_ID\"], \"messages\": [{\"role\": \"user\", \"content\": \"ping\"}]},\n    timeout=15,\n)\nresp.raise_for_status()  # surface auth/network issues here, not mid-workflow","typeGuard":null,"tryCatchPattern":"from src.workflow import WorkflowExecutionError\n\nlast = None\nfor attempt in range(3):\n    try:\n        result = workflow.run(requirement)\n        break\n    except WorkflowExecutionError as e:\n        last = e\n        msg = str(e)\n        transient = any(t in msg for t in (\"429\", \"timeout\", \"Timeout\", \"Connection\"))\n        if not transient or attempt == 2:\n            raise\n        import time; time.sleep(2 ** attempt)\nelse:\n    raise last","preventionTips":["Preflight the LLM provider (one tiny request) before starting multi-stage workflows.","Wrap only transient errors (429/network/timeout) in retries; escalate auth errors immediately.","Log the chained __cause__ (exc) not just the wrapper message, or you lose the root cause.","Set sane timeouts and backoff on the underlying HTTP client."],"tags":["python","llm","workflow","wrapper-exception"],"backgroundTag":null,"analyzedSha":"606a07d341a47be773fab7f4b71177f53f96b2c3","analyzedAt":"2026-08-14T22:57:27.446Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}