PrefectHQ/fastmcp · error · ValueError

ImageContent is only supported in user messages for Anthropi

Error message

ImageContent is only supported in user messages for Anthropic

What it means

ValueError from `_convert_to_anthropic_messages` (list-content branch): Anthropic's API only accepts image blocks in user-role messages, so an ImageContent found in an assistant (or other non-user) message is rejected.

Source

Thrown at fastmcp_slim/fastmcp/client/sampling/handlers/anthropic.py:210

                ] = []

                for item in content:
                    if isinstance(item, ToolUseContent):
                        content_blocks.append(
                            ToolUseBlockParam(
                                type="tool_use",
                                id=item.id,
                                name=item.name,
                                input=item.input,
                            )
                        )
                    elif isinstance(item, TextContent):
                        content_blocks.append(
                            TextBlockParam(type="text", text=item.text)
                        )
                    elif isinstance(item, ImageContent):
                        if message.role != "user":
                            raise ValueError(
                                "ImageContent is only supported in user messages "
                                "for Anthropic"
                            )
                        content_blocks.append(_image_content_to_anthropic_block(item))
                    elif isinstance(item, AudioContent):
                        raise ValueError(
                            "AudioContent is not supported by the Anthropic API"
                        )
                    elif isinstance(item, ToolResultContent):
                        # Extract text content from the result
                        result_content: str | list[TextBlockParam] = ""
                        if item.content:
                            text_blocks: list[TextBlockParam] = [
                                TextBlockParam(type="text", text=sub_item.text)
                                for sub_item in item.content
                                if isinstance(sub_item, TextContent)
                            ]
                            if len(text_blocks) == 1:

View on GitHub (pinned to 1f02114297)

Solutions

  1. Move image content into the user message of the exchange
  2. Drop ImageContent items from non-user messages before invoking the handler
  3. Rewrite the assistant turn as text describing the image
  4. Filter/normalize messages before passing the SamplingMessage list

Example fix

// before
messages = [SamplingMessage(role="assistant", content=[ImageContent(...)]), ...]

// after
messages = [SamplingMessage(role="assistant", content=[TextContent(type="text", text="(image shown to user)")]), ...]
Defensive patterns

Strategy: validation

Validate before calling

def strip_nonuser_images(messages):
    return [
        m if m.role == "user"
        else SamplingMessage(role=m.role, content=[b for b in as_list(m.content) if not isinstance(b, ImageContent)])
        for m in messages
    ]

Type guard

def images_only_in_user_messages(messages) -> bool:
    from mcp.types import ImageContent
    return all(
        m.role == "user" or not any(isinstance(b, ImageContent) for b in as_list(m.content))
        for m in messages
    )

Prevention

When it happens

Trigger: A sampling message with role != "user" whose content is a list containing an ImageContent item; typically assistant messages carrying image blocks in the conversation history passed to the Anthropic handler.

Common situations: Replaying conversation histories where images were attached to assistant turns; multi-turn sampling contexts built by middleware that doesn't enforce role constraints.

Related errors


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