PrefectHQ/fastmcp · error · TypeError
A completion handler returned a str; return a list of string
Error message
A completion handler returned a str; return a list of strings (for example, [value]) or a Completion instead.
What it means
A @mcp.completion handler returned a bare str, which normalize_completion deliberately rejects. A string satisfies Sequence[str] but would iterate into one-character 'candidates' on the wire, so FastMCP raises TypeError instead of silently producing garbage. Valid returns are mcp_types.Completion, list[str]/tuple[str], or None.
Source
Thrown at fastmcp_slim/fastmcp/server/completions.py:69
MAX_COMPLETION_VALUES = 100
def normalize_completion(result: CompletionValues) -> mcp_types.Completion:
"""Coerce a handler's return value into a wire ``Completion``.
A returned ``str`` is rejected: it is almost always a mistake (the value
would iterate into one-character candidates), so it raises rather than
silently producing surprising output.
The MCP contract caps a completion at 100 values, so a longer result is
truncated to the first 100 with ``has_more`` set — a handler that returns
thousands of matches emits a conforming response rather than an oversized
one that strict clients reject.
"""
if result is None:
return mcp_types.Completion(values=[])
if isinstance(result, str):
raise TypeError(
"A completion handler returned a str; return a list of strings "
"(for example, [value]) or a Completion instead."
)
if isinstance(result, mcp_types.Completion):
completion = result
else:
completion = mcp_types.Completion(values=list(result))
if len(completion.values) > MAX_COMPLETION_VALUES:
total = (
completion.total if completion.total is not None else len(completion.values)
)
return mcp_types.Completion(
values=completion.values[:MAX_COMPLETION_VALUES],
total=total,
has_more=True,
)
return completionView on GitHub (pinned to 1f02114297)
Solutions
- Wrap the string in a list: return [value].
- Return None if there are no candidates.
- Return mcp_types.Completion(values=[...]) if you need total/has_more pagination hints.
- Annotate the handler's return type as CompletionValues (str is excluded) so type checkers catch this.
Example fix
// before
@mcp.completion
async def complete(ref, argument, context):
return "my-value"
// after
@mcp.completion
async def complete(ref, argument, context):
return ["my-value"] Defensive patterns
Strategy: type-guard
Validate before calling
def check_completion_result(r):
if r is None or isinstance(r, (mcp_types.Completion, list, tuple)):
return r
if isinstance(r, str):
return [r]
raise TypeError(f"invalid completion return: {type(r).__name__}") Type guard
def is_valid_completion(r) -> bool:
return r is None or isinstance(r, mcp_types.Completion) or (
isinstance(r, (list, tuple)) and all(isinstance(v, str) for v in r)
) Try / catch
try:
result = await handler(ref, arg, ctx)
except TypeError as e:
logging.error("completion handler returned wrong type: %s", e)
result = None # empty completion Prevention
- Annotate completion handlers with the CompletionValues return type so type checkers reject bare str.
- Always return a list, even for a single candidate: [value].
- Add a unit test that invokes each completion handler and asserts is_valid_completion on the result.
When it happens
Trigger: Registering a completion handler via @mcp.completion whose return statement is `return value` (a single string) rather than a list, then receiving a completion/complete request that reaches the handler.
Common situations: Handlers that fetch a single best-match value and return it directly; authors misreading the return type and assuming a str is a valid single-candidate shorthand; refactors that changed a list return into a scalar return.
Related errors
- Cannot specify both a name as first argument and as keyword
- First argument to @{decorator_name} must be a function, stri
- Got unexpected keyword argument(s): {', '.join(sorted(unknow
- Version must be a string, int, or float, got bool: {v!r}
- Version must be a string, int, or float, got {type(v).__name
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/60760a4f62d871db.
Report an issue: GitHub.