{"record":{"id":"08995098d384541b","repo":"mastra-ai/mastra","slug":"saw-text-content-for-input-coremessage-but-the-ro","errorCode":null,"errorMessage":"Saw text content for input CoreMessage, but the role is ${coreMessage.role}. This is only allowed for \"system\", \"assistant\", and \"user\" roles.","messagePattern":"Saw text content for input CoreMessage, but the role is (.+?)\\. This is only allowed for \"system\", \"assistant\", and \"user\" roles\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/core/src/agent/message-list/conversion/to-prompt.ts","lineNumber":29,"sourceCode":"\n/**\n * Convert an AI SDK V4 CoreMessage to a V1 LanguageModel prompt message.\n * Used for creating LLM prompt messages without AI SDK streamText/generateText.\n */\nexport function aiV4CoreMessageToV1PromptMessage(coreMessage: CoreMessageV4): LanguageModelV1Message {\n  if (coreMessage.role === `system`) {\n    return coreMessage;\n  }\n\n  if (typeof coreMessage.content === `string` && (coreMessage.role === `assistant` || coreMessage.role === `user`)) {\n    return {\n      ...coreMessage,\n      content: [{ type: 'text', text: coreMessage.content }],\n    };\n  }\n\n  if (typeof coreMessage.content === `string`) {\n    throw new Error(\n      `Saw text content for input CoreMessage, but the role is ${coreMessage.role}. This is only allowed for \"system\", \"assistant\", and \"user\" roles.`,\n    );\n  }\n\n  const roleContent: {\n    user: Exclude<Extract<LanguageModelV1Message, { role: 'user' }>['content'], string>;\n    assistant: Exclude<Extract<LanguageModelV1Message, { role: 'assistant' }>['content'], string>;\n    tool: Exclude<Extract<LanguageModelV1Message, { role: 'tool' }>['content'], string>;\n  } = {\n    user: [],\n    assistant: [],\n    tool: [],\n  };\n\n  const role = coreMessage.role;\n\n  for (const part of coreMessage.content) {\n    const incompatibleMessage = `Saw incompatible message content part type ${part.type} for message role ${role}`;","sourceCodeStart":11,"sourceCodeEnd":47,"githubUrl":"https://github.com/mastra-ai/mastra/blob/75dd419e613fe9c39f846ffc500716141b74fda6/packages/core/src/agent/message-list/conversion/to-prompt.ts#L11-L47","documentation":"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.","triggerScenarios":"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.","commonSituations":"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'}.","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."],"exampleFix":"// before\n{ role: 'tool', content: '42' }\n// after\n{ role: 'tool', content: [{ type: 'tool-result', toolCallId: 'call_1', toolName: 'getAnswer', result: '42' }] }","handlingStrategy":"validation","validationCode":"function assertToolMessage(m: { role: string; content: unknown }) {\n  if (m.role === 'tool') {\n    if (typeof m.content === 'string') throw new Error('tool messages must have tool-result array content');\n    if (Array.isArray(m.content) && m.content.some((p: any) => p.type !== 'tool-result'))\n      throw new Error('tool messages may only contain tool-result parts');\n  }\n}","typeGuard":"function isConvertiblePromptMessage(m: { role: string; content: unknown }): boolean {\n  if (typeof m.content !== 'string') return true;\n  return m.role === 'system' || m.role === 'assistant' || m.role === 'user';\n}","tryCatchPattern":"try {\n  const prompt = messageList.toPrompt();\n} catch (e) {\n  if (e instanceof Error && e.message.includes('Saw text content for input CoreMessage')) {\n    console.error('Tool message has string content; wrap it in a tool-result part before converting.');\n  } else throw e;\n}","preventionTips":["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()"],"tags":["message-conversion","ai-sdk-v4","tool-messages","prompt"],"backgroundTag":"invalid-message-role-content","analyzedSha":"75dd419e613fe9c39f846ffc500716141b74fda6","analyzedAt":"2026-08-30T00:15:31.844Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}