mastra-ai/mastra · error · Error
Encountered unknown role ${role} when converting V4 CoreMess
Error message
Encountered unknown role ${role} when converting V4 CoreMessage -> V4 LanguageModelV1Prompt, input message: ${JSON.stringify(coreMessage, null, 2)} What it means
At the end of aiV4CoreMessageToV1PromptMessage (to-prompt.ts:170), after all content parts are processed, the role must be one of 'system' | 'user' | 'assistant' | 'tool'. Any other role value falls through every branch and throws with the full input message JSON. Since TypeScript types restrict roles, this usually indicates a runtime value that bypassed the types (any-cast, JSON from storage, wrong SDK version).
Source
Thrown at packages/core/src/agent/message-list/conversion/to-prompt.ts:170
return {
...coreMessage,
content: roleContent[role],
};
}
if (role === `user`) {
return {
...coreMessage,
content: roleContent[role],
};
}
if (role === `assistant`) {
return {
...coreMessage,
content: roleContent[role],
};
}
throw new Error(
`Encountered unknown role ${role} when converting V4 CoreMessage -> V4 LanguageModelV1Prompt, input message: ${JSON.stringify(coreMessage, null, 2)}`,
);
}
/**
* Convert an AI SDK V5 ModelMessage to a V2 LanguageModel prompt message.
* Used for creating LLM prompt messages without AI SDK streamText/generateText.
*/
export function aiV5ModelMessageToV2PromptMessage(modelMessage: AIV5Type.ModelMessage): AIV5LanguageModelV2Message {
if (modelMessage.role === `system`) {
return modelMessage;
}
if (typeof modelMessage.content === `string` && (modelMessage.role === `assistant` || modelMessage.role === `user`)) {
return {
role: modelMessage.role,
content: [{ type: 'text', text: modelMessage.content }],
providerOptions: modelMessage.providerOptions,View on GitHub (pinned to 75dd419e61)
Solutions
- Remap non-standard roles to the four supported ones: 'function'->'tool' (with tool-result parts), 'developer'/'model'->'assistant' or 'system'.
- Validate message roles at your system boundary and reject/normalize unknown values before adding to memory.
- Check for any-casts (`as CoreMessage`) hiding bad role values; let the types do their job.
Example fix
// before
messages.push({ role: 'model', content: 'hi' } as any)
// after
messages.push({ role: 'assistant', content: 'hi' }) Defensive patterns
Strategy: type-guard
Validate before calling
const VALID_ROLES = ['system', 'user', 'assistant', 'tool'] as const;
function assertValidRoles(messages: Array<{ role: string }>) {
for (const m of messages) {
if (!VALID_ROLES.includes(m.role as any))
throw new Error(`Role "${m.role}" not supported; remap to one of ${VALID_ROLES.join(', ')}`);
}
} Type guard
type PromptRole = 'system' | 'user' | 'assistant' | 'tool';
function hasPromptRole(m: unknown): m is { role: PromptRole } {
return typeof m === 'object' && m !== null &&
['system', 'user', 'assistant', 'tool'].includes((m as any).role);
} Try / catch
try {
const prompt = messageList.toPrompt();
} catch (e) {
if (e instanceof Error && e.message.includes('Encountered unknown role')) {
const role = /unknown role (\S+)/.exec(e.message)?.[1];
console.error(`Remap non-standard role "${role}" to system/user/assistant/tool`);
} else throw e;
} Prevention
- Remap foreign role vocabularies ('function', 'developer', 'model') at ingestion
- Avoid `as CoreMessage` any-casts that hide invalid role values
- Validate roles of anything loaded from storage or external APIs before prompt building
When it happens
Trigger: Passing a CoreMessage whose role is a non-standard string (e.g. 'function', 'developer', 'model', or undefined after an any-cast) into the V4 prompt conversion path (directly or via MessageList.toPrompt).
Common situations: Migrating from OpenAI-style roles ('function'/'developer') or other SDKs ('model' in Gemini/Anthropic style) without remapping; deserialized messages with corrupted role fields; mixing AI SDK v3-era message objects into a v4 pipeline.
Related errors
- Saw text content for input CoreMessage, but the role is ${co
- Saw incompatible message content part type ${part.type} for
- Saw text content for input ModelMessage, but the role is ${m
- Unhandled content part type: ${(exhaustiveCheck as { type: s
- Cancelled
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/dde57dacfa0d228c.
Report an issue: GitHub.