datawhalechina/hello-agents · error · WorkflowExecutionError

{stage}失败:工具返回的不是有效 JSON

Error message

{stage}失败:工具返回的不是有效 JSON

What it means

Raised by _run_tool when json.loads(raw_result) fails with TypeError or ValueError — i.e. the tool's return value is not valid JSON text. The workflow parses a strict string protocol: every tool must return a JSON-encoded string (TypeError fires when raw_result is None/non-str, ValueError when the string is malformed JSON).

Source

Thrown at Co-creation-projects/zenith191-RequirementClarifierAgent/src/workflow.py:153

            raise WorkflowExecutionError(f"{stage}阶段返回了空结果")
        return response.strip()

    def _run_tool(
        self, name: str, parameters: dict[str, object], stage: str
    ) -> dict[str, object]:
        """通过官方 ToolRegistry 获取工具并解析其字符串协议。"""

        tool = self.tool_registry.get_tool(name)
        if tool is None:
            raise WorkflowExecutionError(f"{stage}失败:工具 {name} 未注册")
        try:
            raw_result = tool.run(parameters)
        except Exception as exc:
            raise WorkflowExecutionError(f"{stage}失败:工具执行异常:{exc}") from exc
        try:
            payload = json.loads(raw_result)
        except (TypeError, ValueError) as exc:
            raise WorkflowExecutionError(f"{stage}失败:工具返回的不是有效 JSON") from exc
        if not isinstance(payload, dict):
            raise WorkflowExecutionError(f"{stage}失败:工具结果必须是 JSON 对象")
        if not payload.get("ok"):
            raise WorkflowExecutionError(
                f"{stage}失败:{payload.get('message', '未知工具错误')}"
            )
        return payload

    def _clear_agent_histories(self) -> None:
        """避免多次运行时把上一条需求带入下一条需求。"""

        for agent in (
            self.team.analyst,
            self.team.architect,
            self.team.reviewer,
            self.team.synthesizer,
        ):
            clear_history = getattr(agent, "clear_history", None)

View on GitHub (pinned to 606a07d341)

Solutions

  1. Make the tool return json.dumps(payload) — always a JSON-encoded string of an object.
  2. Validate the tool standalone: json.loads(MyTool().run({...})) should succeed before wiring it into the registry.
  3. Keep the payload shape {"ok": bool, ...} so downstream checks (ok / message) also pass.

Example fix

# before
def run(self, params):
    return {"ok": True, "result": 42}  # dict, not JSON string

# after
import json
def run(self, params):
    return json.dumps({"ok": True, "result": 42})
Defensive patterns

Strategy: type-guard

Validate before calling

# contract test every tool must pass before registration
import json

def tool_returns_json_object(tool, sample_params) -> bool:
    raw = tool.run(sample_params)
    try:
        payload = json.loads(raw)
    except (TypeError, ValueError):
        return False
    return isinstance(payload, dict) and "ok" in payload

assert tool_returns_json_object(my_tool, sample_params)

Type guard

import json

def is_json_object_string(raw: object) -> bool:
    """True when raw is a string decoding to a JSON object."""
    if not isinstance(raw, str):
        return False
    try:
        return isinstance(json.loads(raw), dict)
    except ValueError:
        return False

Try / catch

try:
    result = workflow.run(requirement)
except WorkflowExecutionError as e:
    if "不是有效 JSON" in str(e):
        fix_tool_return_type()  # make the tool json.dumps() its result
        result = workflow.run(requirement)
    else:
        raise

Prevention

When it happens

Trigger: A tool returns a Python dict directly (json.loads(dict) → TypeError); a tool returns plain text like 'done' (ValueError); a tool returns JSON with single quotes or trailing commas (ValueError).

Common situations: Writing a custom tool that returns json.dumps(...) inconsistently — or forgets dumps entirely; tools that return pretty-printed or Python-repr output; upgrading a tool that previously returned free text.

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/8a9a2ebd4da51f78. Report an issue: GitHub.