PrefectHQ/fastmcp · error · ValueError
AudioContent is only supported in user messages for OpenAI
Error message
AudioContent is only supported in user messages for OpenAI
What it means
Analogous to the image case: OpenAI chat messages only support audio input parts in user messages, so the handler raises ValueError when AudioContent appears in a sampling message with a non-'user' role during conversion.
Source
Thrown at fastmcp_slim/fastmcp/client/sampling/handlers/openai.py:372
# 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(
role="user",
content=[_audio_content_to_openai_part(content)],
)
)
continue
raise ValueError(f"Unsupported content type: {type(content)}")
return openai_messages
@staticmethod
def _chat_completion_to_create_message_result(
chat_completion: ChatCompletion,
) -> CreateMessageResult:View on GitHub (pinned to 1f02114297)
Solutions
- Move AudioContent into a SamplingMessage with role='user'.
- Replace assistant-role audio with a TextContent transcript before calling the handler.
- Use a provider/handler that supports audio in assistant messages if that is a hard requirement.
- Catch the ValueError and re-encode the message list with audio only in user turns.
Example fix
// before SamplingMessage(role='assistant', content=[AudioContent(data=..., mimeType='audio/wav')]) // after SamplingMessage(role='user', content=[AudioContent(data=..., mimeType='audio/wav')])
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, AudioContent) for c in m.content):
raise ValueError('AudioContent found in non-user message; move it to a user message')
return messages Type guard
def is_user_audio_message(m) -> bool:
return m.role == 'user' and any(isinstance(c, AudioContent) for c in getattr(m, 'content', [])) Try / catch
try:
result = await openai_handler(messages, params)
except ValueError as e:
if 'AudioContent is only supported in user messages' in str(e):
messages = [m for m in messages if not (m.role != 'user' and any(isinstance(c, AudioContent) for c in m.content))]
result = await openai_handler(messages, params)
else:
raise Prevention
- Place AudioContent only in user-role sampling messages
- Convert assistant audio to a text transcript before sampling
- Validate message roles before calling the handler
- Cover audio flows with handler unit tests
When it happens
Trigger: Passing a SamplingMessage with role='assistant' (or other non-'user' role) containing AudioContent through the OpenAI sampling handler's _convert_to_openai_messages.
Common situations: Voice-agent pipelines that place transcribed or raw audio in assistant turns; replayed conversation logs where roles were re-mapped incorrectly; server code that attaches generated audio to assistant messages.
Related errors
- Unsupported audio MIME type for OpenAI: {content.mime_type!r
- ImageContent is only supported in user messages for OpenAI
- Unsupported tool_choice mode: {tool_choice.mode!r}
- AudioContent is not supported by the Anthropic API
- Unsupported image MIME type for OpenAI: {content.mime_type!r
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/1747c3c6e959d072.
Report an issue: GitHub.