datawhalechina/hello-agents · error · WorkflowExecutionError

需求必须是字符串

Error message

需求必须是字符串

What it means

Raised by RequirementClarifierWorkflow._validate_requirement (src/workflow.py:118) when the requirement argument is not a str instance. Since Python type hints are not enforced at runtime, passing bytes, a dict, or None into run() hits this explicit guard instead of failing later with a confusing prompt error.

Source

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

            architecture=architecture,
            risk_review=risk_review,
            report=report,
            quality=quality,
        )

    @staticmethod
    def save_report(result: WorkflowResult, output_path: str | Path) -> Path:
        """以 UTF-8 保存最终 Markdown 报告。"""

        path = Path(output_path)
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(result.report.rstrip() + "\n", encoding="utf-8")
        return path

    @staticmethod
    def _validate_requirement(requirement: str) -> str:
        if not isinstance(requirement, str):
            raise WorkflowExecutionError("需求必须是字符串")
        requirement = requirement.strip()
        if not requirement:
            raise WorkflowExecutionError("需求不能为空")
        if len(requirement) > MAX_REQUIREMENT_LENGTH:
            raise WorkflowExecutionError(
                f"需求文本不能超过 {MAX_REQUIREMENT_LENGTH} 个字符"
            )
        return requirement

    @staticmethod
    def _run_agent(stage: str, agent: AgentLike, prompt: str) -> str:
        try:
            response = agent.run(prompt)
        except Exception as exc:
            raise WorkflowExecutionError(f"{stage}阶段执行失败:{exc}") from exc
        if not isinstance(response, str) or not response.strip():
            raise WorkflowExecutionError(f"{stage}阶段返回了空结果")
        return response.strip()

View on GitHub (pinned to 606a07d341)

Solutions

  1. Convert to str before calling run(): decode bytes, extract the text field from the dict, or str()-serialize deliberately.
  2. Fix the caller to pass requirement['text'] (or equivalent) rather than the wrapper object.
  3. Add an assertion/log at the API boundary so non-string payloads are caught there with better context.

Example fix

# before
result = workflow.run(req.json())  # dict!

# after
result = workflow.run(req.json()["requirement"])
Defensive patterns

Strategy: type-guard

Validate before calling

def is_plain_text(v) -> bool:
    return isinstance(v, str) and v.strip() != ""

# at the API boundary
payload = req.json()
requirement = payload.get("requirement") if isinstance(payload, dict) else None
if not is_plain_text(requirement):
    raise ValueError("'requirement' must be a non-empty string")

Type guard

def is_requirement_text(value: object) -> bool:
    """Narrows to a usable requirement string."""
    return isinstance(value, str) and bool(value.strip())

assert is_requirement_text(requirement), type(requirement)

Try / catch

from src.workflow import WorkflowExecutionError

try:
    result = workflow.run(requirement)
except WorkflowExecutionError as e:
    if "必须是字符串" in str(e):
        requirement = str(requirement)  # deliberate coercion, then retry once
        result = workflow.run(requirement)
    else:
        raise

Prevention

When it happens

Trigger: Calling workflow.run(requirement) with a dict parsed from JSON (e.g. {'text': '...'}), bytes read from a file, None from a failed upstream extraction, or an int/float ID.

Common situations: Gluing the workflow behind an API that decodes JSON and passes the whole request object; reading a file in 'rb' mode and forgetting .decode(); a None default from an optional form field flowing through.

Related errors


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