PrefectHQ/fastmcp · error · TypeError

messages[{i}] must be Message or str, got {type(item).__name

Error message

messages[{i}] must be Message or str, got {type(item).__name__}. Use Message({item!r}) to wrap the value.

What it means

Prompt.convert_result accepts only PromptResult, str, or list of Message/str as a render return value. Inside a list, any item that is not a Message or str raises TypeError at that index, prompting you to wrap it with Message(...).

Source

Thrown at fastmcp_slim/fastmcp/prompts/base.py:340

        if isinstance(raw_value, mcp_types.InputRequiredResult):
            # The prompt asked the client for input (SEP-2322). Wrap it so the
            # ask travels the middleware chain as an ordinary result; the wire
            # handler unwraps it.
            return InputRequiredPromptResult(raw_value)

        if isinstance(raw_value, str):
            return PromptResult(raw_value, description=self.description, meta=self.meta)

        if isinstance(raw_value, list | tuple):
            messages: list[Message] = []
            for i, item in enumerate(raw_value):
                if isinstance(item, Message):
                    messages.append(item)
                elif isinstance(item, str):
                    messages.append(Message(item))
                else:
                    raise TypeError(
                        f"messages[{i}] must be Message or str, got {type(item).__name__}. "
                        f"Use Message({item!r}) to wrap the value."
                    )
            return PromptResult(messages, description=self.description, meta=self.meta)

        raise TypeError(
            f"Prompt must return str, list[Message], or PromptResult, "
            f"got {type(raw_value).__name__}"
        )

    async def _render(
        self,
        arguments: dict[str, Any] | None = None,
    ) -> PromptResult:
        """Server entry point for prompt renders.

        The server calls this method instead of render() directly so that
        subclasses can customize dispatch. For example, FastMCPProviderPrompt

View on GitHub (pinned to 1f02114297)

Solutions

  1. Wrap non-str items in Message(...) inside the returned list
  2. Return a plain str or PromptResult instead of a heterogeneous list
  3. Sanitize the list before returning: [it if isinstance(it,(Message,str)) else Message(str(it)) for it in items]

Example fix

// before
return ["hello", {"role": "user", "content": "bye"}]
// after
return [Message("hello"), Message("bye")]
Defensive patterns

Strategy: type-guard

Validate before calling

def safe_render_list(items) -> list:
    out = []
    for it in items:
        if isinstance(it, (Message, str)):
            out.append(it)
        else:
            out.append(Message(str(it)))
    return out

Type guard

def is_message_or_str_list(v: object) -> bool:
    return isinstance(v, list) and all(isinstance(i, (Message, str)) for i in v)

Try / catch

try:
    result = prompt.convert_result(raw)
except TypeError as e:
    raw = [Message(str(x)) for x in raw]
    result = prompt.convert_result(raw)

Prevention

When it happens

Trigger: A prompt's render() returns a list containing ints, dicts, Messages from a different library, tuples, or None — e.g. return ["hi", {"role": "user", "content": "x"}].

Common situations: Returning LLM SDK message dicts directly from render; returning template parts of mixed types; returning results of splitting a string into non-str objects.

Understand the failure class

Background: "Wrong argument type", "must be a string", "expected Array or Prism::Scope": TypeError and ArgumentError when a library receives a value of the wrong type — this error's family across 28 libraries.

Related errors


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/fa92ce2af84b8b6a. Report an issue: GitHub.