can1357/oh-my-pi · error · ValidationError
Unknown message role
Error message
Unknown message role
What it means
During Bedrock request conversion, each Context message's role is switched over. The converter handles 'user', 'assistant', and the toolResult grouping path; any other role value falls to the default branch and throws a ValidationError. Since Context roles are typed, this normally means a role value outside the expected enum reached the converter (often via untyped/JSON round-tripped data).
Source
Thrown at packages/ai/src/providers/amazon-bedrock.ts:961
toolResult: {
toolUseId: normalizeToolCallId(nextMsg.toolCallId),
content: nextMsg.content.map(c =>
c.type === "image"
? { image: createImageBlock(c.mimeType, c.data) }
: { text: c.text.toWellFormed() },
),
status: nextMsg.isError ? "error" : "success",
},
});
j++;
}
i = j - 1;
result.push({ role: "user", content: toolResults });
break;
}
default:
throw new AIError.ValidationError("Unknown message role");
}
}
// Prioritize the final user checkpoint; buildSystemPrompt consumes any
// remaining configured capacity afterward.
if (result.length > 0) {
const lastMessage = result[result.length - 1];
if (lastMessage.role === "user" && lastMessage.content) {
const cachePoint = takeCachePoint(promptCachePolicy);
if (cachePoint) (lastMessage.content as UserContent[]).push(cachePoint);
}
}
return result;
}
function messagesHaveToolBlocks(messages: WireMessage[]): boolean {
for (const message of messages) {View on GitHub (pinned to 9690622007)
Solutions
- Move system-prompt content into the dedicated system option/parameter instead of a system-role Context message
- Express tool outputs using the library's tool-result message format so the converter's grouping branch handles them
- Validate/normalize roles when loading transcripts from disk or another provider before passing them as Context
- Check for typos or casing drift ('User'/'SYSTEM') in code that constructs messages dynamically
Example fix
// before
context.push({ role: "system", content: [{ type: "text", text: sysPrompt }] });
// after
const context: Context[] = [{ role: "user", content: [...] }];
const system = sysPrompt; // passed as the request's system option Defensive patterns
Strategy: validation
Validate before calling
const VALID_ROLES = new Set(["user", "assistant"]);
function assertValidRoles(context: Context[]): void {
for (const m of context) {
if (!VALID_ROLES.has(m.role as string)) {
throw new Error(`Invalid Context role '${m.role}' — put system prompts in the system option, tool output in tool-result messages`);
}
}
} Type guard
function isContextMessage(m: unknown): m is Context {
return typeof m === "object" && m !== null &&
"role" in m && ((m as Context).role === "user" || (m as Context).role === "assistant");
} Try / catch
try {
await provider.stream(context);
} catch (err) {
if (err instanceof AIError.ValidationError && err.message === "Unknown message role") {
// inspect context roles; usually a 'system'/'tool' message misfiled as a Context message
logger.error("context role error", { roles: context.map(m => m.role) });
}
throw err;
} Prevention
- Never use role 'system' in Context arrays — pass system prompts via the system parameter
- Type message arrays as Context[] so TS catches invalid role literals at compile time
- Validate roles when deserializing transcripts from JSON before use
When it happens
Trigger: Passing a message whose role is not 'user', 'assistant', or a recognized tool-result role into the Bedrock stream/complete call — e.g. a 'system' or 'tool' role supplied as a Context message instead of via the dedicated system option or tool-result message shape.
Common situations: Hand-building context arrays and using role 'system' for a system prompt (which must go in the system parameter); deserializing old/foreign transcript JSON with unexpected role strings; provider-agnostic code that emits provider-specific roles.
Related errors
- Unknown user content type
- Unknown assistant content type
- Unknown image type: ${mimeType}
- Unsupported language '{value}'. Supported: {}
- Unable to infer language from file extension: {}. Specify `l
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/d28fc8517d4d705c.
Report an issue: GitHub.