PrefectHQ/fastmcp · error
Either content or structured_content must be provided
Error message
Either content or structured_content must be provided
What it means
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.
Source
Thrown at fastmcp_slim/fastmcp/tools/base.py:122
meta: dict[str, Any] | None = Field(
default=None, description="Runtime metadata about the tool execution"
)
is_error: bool = Field(
default=False,
description="Whether this result represents a tool execution error. "
"When True, it maps to CallToolResult.is_error so the error is returned "
"to the client rather than raised.",
)
def __init__(
self,
content: list[ContentBlock] | Any | None = None,
structured_content: dict[str, Any] | Any | None = None,
meta: dict[str, Any] | None = None,
is_error: bool = False,
):
if content is None and structured_content is None:
raise ValueError("Either content or structured_content must be provided")
elif content is None:
content = structured_content
converted_content: list[ContentBlock] = _convert_to_content(result=content)
if structured_content is not None:
# Convert Prefab types to their wire-format envelope before
# generic serialization, so the renderer gets the right shape.
if is_prefab_app(structured_content):
structured_content = _prefab_to_json(structured_content)
elif is_prefab_component(structured_content):
structured_content = _prefab_to_json(
prefab_app_from_component(structured_content)
)
try:
structured_content = _serialize_to_jsonable(structured_content)
except pydantic_core.PydanticSerializationError as e:View on GitHub (pinned to 1f02114297)
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.
Example fix
# before return ToolResult(content=None, structured_content=None) # after return ToolResult(content="No results found.")
Defensive patterns
Strategy: validation
Validate before calling
def safe_tool_result(content=None, structured_content=None, **kw):
if content is None and structured_content is None:
content = "" # or raise/handle per your contract
return ToolResult(content=content, structured_content=structured_content, **kw) Try / catch
try:
result = ToolResult(content=fn_output, structured_content=sc)
except ValueError:
result = ToolResult(content="(no output)") Prevention
- 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.
When it happens
Trigger: `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).
Common situations: 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.
Related errors
- structured_content must be a dict or None. Got {type(structu
- Cannot resolve tool reference: {fn!r}
- Cannot determine tool name for {fn!r}
- File {name!r} not found. Available: {available}
- File {f.get('name', '?')!r} exceeds max size ({_format_size(
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/9fb4fdf3fcd17461.
Report an issue: GitHub.