{"record":{"id":"9fb4fdf3fcd17461","repo":"PrefectHQ/fastmcp","slug":"either-content-or-structured-content-must-be-provi","errorCode":null,"errorMessage":"Either content or structured_content must be provided","messagePattern":"Either content or structured_content must be provided","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"fastmcp_slim/fastmcp/tools/base.py","lineNumber":122,"sourceCode":"    meta: dict[str, Any] | None = Field(\n        default=None, description=\"Runtime metadata about the tool execution\"\n    )\n    is_error: bool = Field(\n        default=False,\n        description=\"Whether this result represents a tool execution error. \"\n        \"When True, it maps to CallToolResult.is_error so the error is returned \"\n        \"to the client rather than raised.\",\n    )\n\n    def __init__(\n        self,\n        content: list[ContentBlock] | Any | None = None,\n        structured_content: dict[str, Any] | Any | None = None,\n        meta: dict[str, Any] | None = None,\n        is_error: bool = False,\n    ):\n        if content is None and structured_content is None:\n            raise ValueError(\"Either content or structured_content must be provided\")\n        elif content is None:\n            content = structured_content\n\n        converted_content: list[ContentBlock] = _convert_to_content(result=content)\n\n        if structured_content is not None:\n            # Convert Prefab types to their wire-format envelope before\n            # generic serialization, so the renderer gets the right shape.\n            if is_prefab_app(structured_content):\n                structured_content = _prefab_to_json(structured_content)\n            elif is_prefab_component(structured_content):\n                structured_content = _prefab_to_json(\n                    prefab_app_from_component(structured_content)\n                )\n\n            try:\n                structured_content = _serialize_to_jsonable(structured_content)\n            except pydantic_core.PydanticSerializationError as e:","sourceCodeStart":104,"sourceCodeEnd":140,"githubUrl":"https://github.com/PrefectHQ/fastmcp/blob/1f021142978e0861cd910c8df4e8074bc7cf3978/fastmcp_slim/fastmcp/tools/base.py#L104-L140","documentation":"ToolResult requires at least one of `content` or `structured_content`. A result with neither would be an empty tool response, which MCP tools should never emit; the constructor raises ValueError to catch this at construction time rather than sending an empty result to the client.","triggerScenarios":"`ToolResult()` with no arguments, or `ToolResult(content=None, structured_content=None)` — usually from a tool function returning None, or code constructing a result from variables that both ended up None (e.g. an unhandled empty branch).","commonSituations":"Tool functions that fall through without returning; wrappers that forward an optional value which is None; conditional result-building code where every branch forgot to set a payload.","solutions":["Return an explicit value from the tool (e.g. a string message or dict) instead of None.","For intentionally empty results, pass a sentinel like `content=\"\"` or an empty dict for structured_content.","Ensure tools declare an output_schema so unstructured None returns are wrapped/validated instead of passed through.","In result-building code, assert/log when both inputs are None before constructing ToolResult."],"exampleFix":"# before\nreturn ToolResult(content=None, structured_content=None)\n# after\nreturn ToolResult(content=\"No results found.\")","handlingStrategy":"validation","validationCode":"def safe_tool_result(content=None, structured_content=None, **kw):\n    if content is None and structured_content is None:\n        content = \"\"  # or raise/handle per your contract\n    return ToolResult(content=content, structured_content=structured_content, **kw)","typeGuard":null,"tryCatchPattern":"try:\n    result = ToolResult(content=fn_output, structured_content=sc)\nexcept ValueError:\n    result = ToolResult(content=\"(no output)\")","preventionTips":["Ensure tool functions always return a value; avoid bare `pass`/fall-through branches.","Add type hints so None returns are caught by mypy/pyright.","Coerce None to a message string at the tool boundary."],"tags":["python","valueerror","tool-result","empty-response","mcp"],"backgroundTag":"empty-required-field","analyzedSha":"1f021142978e0861cd910c8df4e8074bc7cf3978","analyzedAt":"2026-08-29T14:31:16.082Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}