mastra-ai/mastra · error
Encountered unknown role ${role} when converting V5 ModelMes
Error message
Encountered unknown role ${role} when converting V5 ModelMessage -> V5 LanguageModelV2Message, input message: ${JSON.stringify(modelMessage, null, 2)} What it means
aiV5ModelMessageToV2PromptMessage ends with a catch-all throw when the message role is not one of 'system' | 'user' | 'assistant' | 'tool'. Any other role string cannot be mapped to a LanguageModelV2 prompt message, so the raw message is dumped in the error for debugging.
Source
Thrown at packages/core/src/agent/message-list/conversion/to-prompt.ts:306
return {
...modelMessage,
content: roleContent[role],
};
}
if (role === `user`) {
return {
...modelMessage,
content: roleContent[role],
};
}
if (role === `assistant`) {
return {
...modelMessage,
content: roleContent[role],
};
}
throw new Error(
`Encountered unknown role ${role} when converting V5 ModelMessage -> V5 LanguageModelV2Message, input message: ${JSON.stringify(modelMessage, null, 2)}`,
);
}
/**
* Convert tool-result `media` parts in a V2 (AI SDK v5 / spec `v2`) prompt
* using a caller-provided target content-part shape.
*
* Mastra's `toModelOutput` and the vendored AI SDK v5 use `{ type: 'media' }`
* as the authored multimodal tool-result content type. Newer AI SDK provider
* specs use different content-part shapes, so callers provide the target
* conversion for their provider spec.
*/
function convertToolResultContent(
prompt: LanguageModelV2Prompt,
convertMediaPart: (contentPart: Record<string, unknown>, mediaType: string) => unknown,
): LanguageModelV2Prompt {
return prompt.map(message => {View on GitHub (pinned to 75dd419e61)
Solutions
- Normalize roles to one of 'system' | 'user' | 'assistant' | 'tool' before adding to MessageList
- Map framework-specific roles ('human'->'user', 'ai'->'assistant') during ingestion
- Inspect the JSON in the error message to find the offending message and fix its role at the source
- Validate input messages in your ingestion layer
Example fix
// before
{ role: 'human', content: 'hi' }
// after
{ role: 'user', content: 'hi' } Defensive patterns
Strategy: validation
Validate before calling
const ROLES = ['system','user','assistant','tool'];
if (!ROLES.includes(msg.role)) throw new Error(`Unsupported role ${msg.role}`); Type guard
function isKnownRole(r: string): r is 'system'|'user'|'assistant'|'tool' { return ['system','user','assistant','tool'].includes(r); } Try / catch
try { messageList.add(msg); } catch (e) { if (e instanceof Error && e.message.includes('Encountered unknown role')) { const normalized = ROLE_MAP[msg.role] ?? 'user'; messageList.add({ ...msg, role: normalized }); } else throw e; } Prevention
- Normalize foreign role vocabularies at ingestion
- Never trust role strings from external JSON without validation
- Keep a role-mapping table for imported frameworks
When it happens
Trigger: Passing a message with a typo'd or nonstandard role (e.g. 'function', 'developer', 'Assistant', 'human') to MessageList.add() and then generating a prompt with an AI v5 model.
Common situations: Porting from other frameworks (LangChain 'human'/'ai' roles, OpenAI 'developer' role); typos in hand-built histories; deserialized DB rows with corrupted role fields.
Related errors
- Saw text content for input ModelMessage, but the role is ${m
- Unhandled content part type: ${(exhaustiveCheck as { type: s
- INVALID_SYSTEM_MESSAGE_FORMAT
- MASTRA_AIV5_DATA_PART_INVALID
- Unhandled toolInvocation.state: ${String(part.toolInvocation
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/1e43f4d336bc307b.
Report an issue: GitHub.