ruvnet/ruflo · error · Error
Unknown role: ${m.role}
Error message
Unknown role: ${m.role} What it means
While mapping chat messages into WASM ChatMessageWasm objects, the switch statement only accepts roles 'system', 'user', and 'assistant'. Any other role string falls through to a default branch that throws, naming the offending role. This fires per-message during a messages.map(), so the throw aborts the whole batch and no template formatting happens.
Source
Thrown at v3/@claude-flow/cli/src/ruvector/ruvllm-wasm.ts:361
phi: () => mod.ChatTemplateWasm.phi(),
gemma: () => mod.ChatTemplateWasm.gemma(),
};
const factory = presets[template];
if (!factory) throw new Error(`Unknown template preset: ${template}. Use: ${Object.keys(presets).join(', ')}`);
tmpl = factory();
} else if ('custom' in template) {
tmpl = mod.ChatTemplateWasm.custom(template.custom);
} else if ('modelId' in template) {
tmpl = mod.ChatTemplateWasm.detectFromModelId(template.modelId);
}
// Build messages
const wasmMessages = messages.map(m => {
switch (m.role) {
case 'system': return mod.ChatMessageWasm.system(m.content);
case 'user': return mod.ChatMessageWasm.user(m.content);
case 'assistant': return mod.ChatMessageWasm.assistant(m.content);
default: throw new Error(`Unknown role: ${m.role}`);
}
});
return tmpl.format(wasmMessages);
}
// ── KV Cache ─────────────────────────────────────────────────
/**
* Create a KV cache for token management.
*/
export async function createKvCache(opts?: {
tailLength?: number;
maxTokens?: number;
numKvHeads?: number;
headDim?: number;
}): Promise<{
append: (keys: Float32Array, values: Float32Array) => void;View on GitHub (pinned to 6b01dc5a68)
Solutions
- Pre-filter messages to only system/user/assistant before calling the formatter.
- Map or drop unsupported roles (e.g., collapse 'tool'/'function' messages into an 'assistant' message, or omit them).
- Validate the role field at your API boundary and reject early with a clear error naming the allowed set.
Example fix
// before
const out = formatChatMessages(rawOpenAIMessages); // throws on role:'tool'
// after: project to supported roles
const supported = new Set(['system', 'user', 'assistant']);
const safe = rawOpenAIMessages
.filter(m => supported.has(m.role))
.map(m => ({ role: m.role, content: m.content }));
const out = formatChatMessages(safe); Defensive patterns
Strategy: validation
Validate before calling
const ALLOWED_ROLES = new Set(['system', 'user', 'assistant']);
function sanitizeMessages(messages) {
const bad = messages.find(m => !ALLOWED_ROLES.has(m.role));
if (bad) throw new Error(`Unsupported role '${bad.role}'. Allowed: system, user, assistant.`);
return messages;
} Type guard
type SupportedRole = 'system' | 'user' | 'assistant';
function isSupportedRole(r: string): r is SupportedRole {
return r === 'system' || r === 'user' || r === 'assistant';
} Prevention
- Project provider payloads (OpenAI tool/function, Gemini model) to the three supported roles at the boundary.
- Reject unknown roles early rather than letting the formatter throw mid-batch.
- Keep a single mapping layer between external role vocabularies and the WASM roles.
When it happens
Trigger: Passing a message with role 'tool', 'function', 'developer' (OpenAI newer role), 'model' (Gemini naming), or 'system'/'user' with a typo like 'asistant'. Also forwarding raw provider payloads that include tool-call result messages.
Common situations: Translating OpenAI chat completions payloads (which include tool/function messages) into the WASM formatter; mixing Gemini 'model' role naming; user-supplied role strings from a config file.
Related errors
- Unknown template preset: ${template}. Use: ${Object.keys(pre
- Invalid embedding value at index ${i}: expected finite numbe
- unknown game "${key}". Known: ${Object.keys(GAMES).join(', '
- unknown strategy "${name}". Available: ${roster.map((r) => r
- Invalid completion type
AI-assisted analysis of ruvnet/ruflo@6b01dc5a68 (2026-08-12).
Data as JSON: /api/errors/ee3452c4c28d6959.
Report an issue: GitHub.