{"record":{"id":"ee3452c4c28d6959","repo":"ruvnet/ruflo","slug":"unknown-role-m-role","errorCode":null,"errorMessage":"Unknown role: ${m.role}","messagePattern":"Unknown role: (.+?)","errorType":"exception","errorClass":"Error","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/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/ruvector/ruvllm-wasm.ts#L343-L379","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nconst out = formatChatMessages(rawOpenAIMessages); // throws on role:'tool'\n\n// after: project to supported roles\nconst supported = new Set(['system', 'user', 'assistant']);\nconst safe = rawOpenAIMessages\n  .filter(m => supported.has(m.role))\n  .map(m => ({ role: m.role, content: m.content }));\nconst out = formatChatMessages(safe);","handlingStrategy":"validation","validationCode":"const ALLOWED_ROLES = new Set(['system', 'user', 'assistant']);\nfunction sanitizeMessages(messages) {\n  const bad = messages.find(m => !ALLOWED_ROLES.has(m.role));\n  if (bad) throw new Error(`Unsupported role '${bad.role}'. Allowed: system, user, assistant.`);\n  return messages;\n}","typeGuard":"type SupportedRole = 'system' | 'user' | 'assistant';\nfunction isSupportedRole(r: string): r is SupportedRole {\n  return r === 'system' || r === 'user' || r === 'assistant';\n}","tryCatchPattern":null,"preventionTips":["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."],"tags":["chat-message","llm","validation","ruvector"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}