PrefectHQ/fastmcp · error · ValueError

ImageContent is only supported in user messages for OpenAI

Error message

ImageContent is only supported in user messages for OpenAI

What it means

The OpenAI sampling handler converts MCP sampling messages to OpenAI chat format. OpenAI's chat API only accepts image parts in messages with role 'user'; the handler therefore rejects any ImageContent found in a message whose role is not 'user' (e.g. 'assistant'). It throws a plain ValueError before making any API call.

Source

Thrown at fastmcp_slim/fastmcp/client/sampling/handlers/openai.py:358

                    openai_messages.append(
                        ChatCompletionUserMessageParam(
                            role="user",
                            content=content.text,
                        )
                    )
                else:
                    openai_messages.append(
                        ChatCompletionAssistantMessageParam(
                            role="assistant",
                            content=content.text,
                        )
                    )
                continue

            # Handle ImageContent
            if isinstance(content, ImageContent):
                if message.role != "user":
                    raise ValueError(
                        "ImageContent is only supported in user messages for OpenAI"
                    )
                openai_messages.append(
                    ChatCompletionUserMessageParam(
                        role="user",
                        content=[_image_content_to_openai_part(content)],
                    )
                )
                continue

            # Handle AudioContent
            if isinstance(content, AudioContent):
                if message.role != "user":
                    raise ValueError(
                        "AudioContent is only supported in user messages for OpenAI"
                    )
                openai_messages.append(
                    ChatCompletionUserMessageParam(

View on GitHub (pinned to 1f02114297)

Solutions

  1. Move the ImageContent into a SamplingMessage with role='user' before passing the request to the OpenAI sampling handler.
  2. Drop or convert non-user ImageContent items to TextContent (e.g. a placeholder or caption) before invoking the handler.
  3. If images must appear in assistant turns, use a handler/provider that supports them; OpenAI chat completions does not.
  4. Wrap sampling calls in try/except ValueError and rebuild the message list with only user-role images on failure.

Example fix

// before
SamplingMessage(role='assistant', content=[ImageContent(data=..., mimeType='image/png')])
// after
SamplingMessage(role='user', content=[ImageContent(data=..., mimeType='image/png')])
Defensive patterns

Strategy: validation

Validate before calling

def validate_openai_sampling_messages(messages):
    for m in messages:
        if m.role != 'user' and any(isinstance(c, ImageContent) for c in m.content):
            raise ValueError('ImageContent found in non-user message; move it to a user message')
    return messages

Type guard

def is_user_image_message(m) -> bool:
    return m.role == 'user' and any(isinstance(c, ImageContent) for c in getattr(m, 'content', []))

Try / catch

try:
    result = await openai_handler(messages, params)
except ValueError as e:
    if 'ImageContent is only supported in user messages' in str(e):
        messages = [m for m in messages if is_user_image_message(m) or not any(isinstance(c, ImageContent) for c in m.content)]
        result = await openai_handler(messages, params)
    else:
        raise

Prevention

When it happens

Trigger: Calling OpenAI sampling handler with a SamplingMessage whose role is 'assistant' (or any non-'user' role) and whose content list contains an ImageContent item during _convert_to_openai_messages.

Common situations: Custom server sampling handlers that echo images back in assistant messages; middleware or proxies that preserve the original message roles but attach screenshots or generated images to assistant turns; hand-built sampling request histories where role assignment is wrong.

Related errors


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