mastra-ai/mastra · error · Error
Saw text content for input CoreMessage, but the role is ${co
Error message
Saw text content for input CoreMessage, but the role is ${coreMessage.role}. This is only allowed for "system", "assistant", and "user" roles. What it means
aiV4CoreMessageToV1PromptMessage (to-prompt.ts:29) converts an AI SDK V4 CoreMessage into a LanguageModelV1 prompt message. String content is only convertible for roles 'system', 'assistant', and 'user' — those are special-cased into a text part. If content is a string but the role is anything else (i.e. 'tool'), the converter cannot wrap it and throws.
Source
Thrown at packages/core/src/agent/message-list/conversion/to-prompt.ts:29
/**
* Convert an AI SDK V4 CoreMessage to a V1 LanguageModel prompt message.
* Used for creating LLM prompt messages without AI SDK streamText/generateText.
*/
export function aiV4CoreMessageToV1PromptMessage(coreMessage: CoreMessageV4): LanguageModelV1Message {
if (coreMessage.role === `system`) {
return coreMessage;
}
if (typeof coreMessage.content === `string` && (coreMessage.role === `assistant` || coreMessage.role === `user`)) {
return {
...coreMessage,
content: [{ type: 'text', text: coreMessage.content }],
};
}
if (typeof coreMessage.content === `string`) {
throw new Error(
`Saw text content for input CoreMessage, but the role is ${coreMessage.role}. This is only allowed for "system", "assistant", and "user" roles.`,
);
}
const roleContent: {
user: Exclude<Extract<LanguageModelV1Message, { role: 'user' }>['content'], string>;
assistant: Exclude<Extract<LanguageModelV1Message, { role: 'assistant' }>['content'], string>;
tool: Exclude<Extract<LanguageModelV1Message, { role: 'tool' }>['content'], string>;
} = {
user: [],
assistant: [],
tool: [],
};
const role = coreMessage.role;
for (const part of coreMessage.content) {
const incompatibleMessage = `Saw incompatible message content part type ${part.type} for message role ${role}`;View on GitHub (pinned to 75dd419e61)
Solutions
- Give tool-role messages array content: [{ type: 'tool-result', toolCallId, toolName, result }].
- If the string is a plain remark, change the role to 'user' or 'assistant' instead of 'tool'.
- Sanitize persisted history so tool messages always store structured tool-result parts.
Example fix
// before
{ role: 'tool', content: '42' }
// after
{ role: 'tool', content: [{ type: 'tool-result', toolCallId: 'call_1', toolName: 'getAnswer', result: '42' }] } Defensive patterns
Strategy: validation
Validate before calling
function assertToolMessage(m: { role: string; content: unknown }) {
if (m.role === 'tool') {
if (typeof m.content === 'string') throw new Error('tool messages must have tool-result array content');
if (Array.isArray(m.content) && m.content.some((p: any) => p.type !== 'tool-result'))
throw new Error('tool messages may only contain tool-result parts');
}
} Type guard
function isConvertiblePromptMessage(m: { role: string; content: unknown }): boolean {
if (typeof m.content !== 'string') return true;
return m.role === 'system' || m.role === 'assistant' || m.role === 'user';
} Try / catch
try {
const prompt = messageList.toPrompt();
} catch (e) {
if (e instanceof Error && e.message.includes('Saw text content for input CoreMessage')) {
console.error('Tool message has string content; wrap it in a tool-result part before converting.');
} else throw e;
} Prevention
- Never give 'tool'-role messages string content
- Always pair tool-call parts with matching toolCallId tool-result parts in tool messages
- Validate history loaded from storage before prompt building
- Write a unit test over your persisted message shapes exercising toPrompt()
When it happens
Trigger: Calling the converter (directly or via MessageList.toPrompt / prompt building) with a CoreMessage of role 'tool' whose content is a plain string instead of the required array of tool-result parts.
Common situations: Hand-building tool messages; loading legacy/serialized history where tool messages were flattened to strings; a migration from another framework that models tool replies as {role:'tool',content:'result text'}.
Related errors
- Saw incompatible message content part type ${part.type} for
- Encountered unknown role ${role} when converting V4 CoreMess
- 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/08995098d384541b.
Report an issue: GitHub.