PrefectHQ/fastmcp · error · ValueError
Dict response_type cannot be empty.
Error message
Dict response_type cannot be empty.
What it means
FastMCP supports a dict shorthand for ctx.elicit() response_type to build a single-select titled enum (e.g. {"low": {"title": "Low"}}). Passing an empty dict gives the parser nothing to build an enum schema from, so it raises immediately rather than emitting a degenerate schema to the client.
Source
Thrown at fastmcp_slim/fastmcp/server/elicitation.py:230
if title is not None:
value_schema["title"] = title
if description is not None:
value_schema["description"] = description
def _is_scalar_type(response_type: Any) -> bool:
"""Check if response_type is a scalar type that needs wrapping."""
return (
response_type in {bool, int, float, str}
or get_origin(response_type) is Literal
or (isinstance(response_type, type) and issubclass(response_type, Enum))
)
def _parse_dict_syntax(d: dict[str, Any]) -> ElicitConfig:
"""Parse dict syntax: {"low": {"title": "..."}} -> single-select titled."""
if not d:
raise ValueError("Dict response_type cannot be empty.")
enum_schema = _dict_to_enum_schema(d, multi_select=False)
return ElicitConfig(
schema={
"type": "object",
"properties": {"value": enum_schema},
"required": ["value"],
},
response_type=None,
is_raw=True,
)
def _parse_list_syntax(lst: list[Any]) -> ElicitConfig:
"""Parse list patterns: [[...]], [{...}], or [...]."""
# [["a", "b", "c"]] -> multi-select untitled
if (
len(lst) == 1
and isinstance(lst[0], list)View on GitHub (pinned to 1f02114297)
Solutions
- Populate the dict with at least one option before calling ctx.elicit(), e.g. {"low": {"title": "Low"}}
- Guard the call: only elicit when the options dict is non-empty, otherwise skip the elicitation or return early
- If no options exist, reconsider the flow — eliciting a choice with zero choices is not meaningful
Example fix
// before
await ctx.elicit(response_type={})
// after
choices = {"low": {"title": "Low"}, "high": {"title": "High"}}
if not choices:
raise ValueError("No choices available")
await ctx.elicit(response_type=choices) Defensive patterns
Strategy: validation
Validate before calling
def validate_options(options):
if not isinstance(options, dict) or not options:
raise ValueError("elicit dict response_type must have at least one option")
return options
# call before eliciting
validate_options(choices)
await ctx.elicit(response_type=choices) Type guard
def is_nonempty_str_dict(d: object) -> bool:
return isinstance(d, dict) and len(d) > 0 Try / catch
try:
result = await ctx.elicit(response_type=choices)
except ValueError as e:
if "cannot be empty" in str(e):
logger.error("elicitation options were empty; skipping prompt")
return None
raise Prevention
- Never pass a literal {} as response_type
- Validate dynamically built option dicts are non-empty before eliciting
- Add a unit test covering the empty-options path of your elicitation flow
When it happens
Trigger: Calling ctx.elicit(response_type={}) or parse_elicit_response_type({}) — a dict response_type with zero keys.
Common situations: Building the options dict dynamically from data (e.g. from a DB or config) that came back empty; a typo like response_type=options where options was never populated.
Related errors
- Invalid list response_type format. Received: {lst}
- response_title and response_description are only supported w
- Elicitation responses must be serializable as a JSON object
- mcp_tool() got unexpected keyword argument(s): {sorted(unkno
- mcp_resource() got unexpected keyword argument(s): {sorted(u
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/3a7731e8420fc605.
Report an issue: GitHub.