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

  1. Remap non-standard roles to the four supported ones: 'function'->'tool' (with tool-result parts), 'developer'/'model'->'assistant' or 'system'.
  2. Validate message roles at your system boundary and reject/normalize unknown values before adding to memory.
  3. 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

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


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/dde57dacfa0d228c. Report an issue: GitHub.