{"record":{"id":"ee3452c4c28d6959","repo":"ruvnet/ruflo","slug":"unknown-role-m-role","errorCode":null,"errorMessage":"Unknown role: ${m.role}","messagePattern":"Unknown role: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/ruvector/ruvllm-wasm.ts","lineNumber":361,"sourceCode":"      phi: () => mod.ChatTemplateWasm.phi(),\n      gemma: () => mod.ChatTemplateWasm.gemma(),\n    };\n    const factory = presets[template];\n    if (!factory) throw new Error(`Unknown template preset: ${template}. Use: ${Object.keys(presets).join(', ')}`);\n    tmpl = factory();\n  } else if ('custom' in template) {\n    tmpl = mod.ChatTemplateWasm.custom(template.custom);\n  } else if ('modelId' in template) {\n    tmpl = mod.ChatTemplateWasm.detectFromModelId(template.modelId);\n  }\n\n  // Build messages\n  const wasmMessages = messages.map(m => {\n    switch (m.role) {\n      case 'system': return mod.ChatMessageWasm.system(m.content);\n      case 'user': return mod.ChatMessageWasm.user(m.content);\n      case 'assistant': return mod.ChatMessageWasm.assistant(m.content);\n      default: throw new Error(`Unknown role: ${m.role}`);\n    }\n  });\n\n  return tmpl.format(wasmMessages);\n}\n\n// ── KV Cache ─────────────────────────────────────────────────\n\n/**\n * Create a KV cache for token management.\n */\nexport async function createKvCache(opts?: {\n  tailLength?: number;\n  maxTokens?: number;\n  numKvHeads?: number;\n  headDim?: number;\n}): Promise<{\n  append: (keys: Float32Array, values: Float32Array) => void;","sourceCodeStart":343,"sourceCodeEnd":379,"githubUrl":"https://github.com/ruvnet/ruflo/blob/fa13ee4ad60ac2090b1480656eb233521790d640/v3/@claude-flow/cli/src/ruvector/ruvllm-wasm.ts#L343-L379","documentation":"When formatting a chat prompt, the wrapper maps each message role to a wasm constructor and supports exactly three: system, user, assistant. Any other role value falls through the switch and throws — the constructor set literally has no branch for it.","triggerScenarios":"Passing OpenAI-style histories containing 'tool' or 'function' role messages; a 'developer' or 'system2' role from a newer chat API; role field undefined/misspelled ('Assistant', 'bot'); adapters forwarding raw provider payloads without role normalization.","commonSituations":"Replaying captured LLM conversations that include tool calls; converting chat logs between providers; schema drift when a downstream API adds a new role this wrapper version predates.","solutions":["Normalize before calling: map 'tool'/'function' messages to user (prefix content with tool name) or assistant, and drop roles you cannot represent","Filter or fold unsupported roles in your adapter layer so only system|user|assistant reach the formatter","Validate roles at the boundary with a Set of the three supported values","Upgrade the package if a newer version added the role you need"],"exampleFix":"// before\nconst text = await formatChat(tmpl, rawMessages); // rawMessages has {role:'tool'} → throws\n// after\nconst SUPPORTED = new Set(['system', 'user', 'assistant']);\nconst msgs = rawMessages\n  .map(m => SUPPORTED.has(m.role) ? m : { role: 'user' as const, content: `[${m.role}] ${m.content}` })\n  .filter(m => m.role !== undefined);\nconst text = await formatChat(tmpl, msgs);","handlingStrategy":"type-guard","validationCode":"const SUPPORTED_ROLES = new Set(['system', 'user', 'assistant']);\nconst safeMessages = messages\n  .filter(m => m && typeof m.content === 'string')\n  .map(m => SUPPORTED_ROLES.has(m.role) ? m : { role: 'user' as const, content: `[${m.role}] ${m.content}` });","typeGuard":"type SupportedRole = 'system' | 'user' | 'assistant';\nfunction isSupportedRole(m: { role: string }): m is { role: SupportedRole; content: string } {\n  return m.role === 'system' || m.role === 'user' || m.role === 'assistant';\n}","tryCatchPattern":"try {\n  text = await formatChat(tmpl, messages);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith('Unknown role')) {\n    text = await formatChat(tmpl, normalizeRoles(messages)); // fold, then retry\n  } else throw e;\n}","preventionTips":["Normalize at your adapter boundary: fold tool/function/developer roles into user or assistant before formatting","Reject or log unknown roles at ingestion instead of inside the prompt builder","Keep the supported-role Set in one shared constant so validation and formatting cannot drift"],"tags":["wasm","llm","chat-template","message-roles","invalid-enum"],"backgroundTag":"invalid-enum-value","analyzedSha":"fa13ee4ad60ac2090b1480656eb233521790d640","analyzedAt":"2026-08-18T21:34:22.708Z","contentChangedAt":"2026-08-18T21:34:22.708Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}