PrefectHQ/fastmcp · error · ValueError
Invalid message role: {message.role}
Error message
Invalid message role: {message.role} What it means
_convert_messages_to_google_genai_content raises ValueError when a sampling message has a role other than 'user' or 'assistant'. Google GenAI Content only defines user/model roles, so any other role (e.g. a future or malformed MCP role) cannot be mapped.
Source
Thrown at fastmcp_slim/fastmcp/client/sampling/handlers/google_genai.py:297
"""Convert MCP messages to Google GenAI content."""
google_messages: list[Content] = []
for message in messages:
content = message.content
# Handle list content (tool calls + results)
if isinstance(content, list):
parts: list[Part] = [
_sampling_content_to_google_genai_part(item) for item in content
]
if message.role == "user":
google_messages.append(UserContent(parts=parts))
elif message.role == "assistant":
google_messages.append(ModelContent(parts=parts))
else:
msg = f"Invalid message role: {message.role}"
raise ValueError(msg)
continue
# Handle single content item
part = _sampling_content_to_google_genai_part(content)
if message.role == "user":
google_messages.append(UserContent(parts=[part]))
elif message.role == "assistant":
google_messages.append(ModelContent(parts=[part]))
else:
msg = f"Invalid message role: {message.role}"
raise ValueError(msg)
return google_messages
def _get_candidate_from_response(response: GenerateContentResponse) -> Candidate:
"""Extract the first candidate from a response."""View on GitHub (pinned to 1f02114297)
Solutions
- Use only 'user' or 'assistant' roles in sampling messages; move system text to SamplingParams.systemPrompt.
- Fix the server side if it emits non-standard roles.
- Catch ValueError and remap unknown roles to 'user' in your sampling callback.
- Upgrade fastmcp-slim if a new MCP role needs official mapping.
Example fix
// before SamplingMessage(role="system", content=TextContent(type="text", text="Be concise")) // after SamplingMessage(role="user", content=TextContent(type="text", text="Be concise")) # or set params.systemPrompt = "Be concise"
Defensive patterns
Strategy: validation
Validate before calling
def validate_roles(messages):
for m in messages:
if m.role not in ("user", "assistant"):
raise ValueError(f"Invalid role for Gemini handler: {m.role!r}") Type guard
def has_valid_role(m) -> bool:
return getattr(m, "role", None) in ("user", "assistant") Try / catch
try:
result = await handler(messages, params, context)
except ValueError as e:
if "Invalid message role" in str(e):
messages = [m if m.role in ("user", "assistant") else m.model_copy(update={"role": "user"}) for m in messages]
result = await handler(messages, params, context)
else:
raise Prevention
- Never use 'system' role in sampling messages — use params.systemPrompt
- Only construct SamplingMessage with 'user' or 'assistant'
- Validate server-emitted sampling requests before handling
- Watch MCP spec changes for new roles and update fastmcp-slim
When it happens
Trigger: A sampling request message has role set to something other than the MCP-standard 'user' or 'assistant' — e.g. hand-built SamplingMessage with a typo like 'system' or 'tool'.
Common situations: Constructing SamplingMessage manually with 'system' role (system prompts belong in params.systemPrompt, not messages); copying roles from OpenAI-style payloads; MCP spec changes.
Related errors
- Unsupported content type: {type(content)}
- ImageContent is only supported in user messages for Anthropi
- Unsupported content type: {type(content)}
- Unsupported tool_choice mode: {tool_choice.mode!r}
- Unsupported tool result content type: {type(item).__name__}
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/44d48cabe48431d9.
Report an issue: GitHub.