ruvnet/ruflo · error

Unknown template preset

Error message

Unknown template preset: ${template}. Use: ${Object.keys(presets).join(', ')}

What it means

The chat-templating wrapper accepts a template either as one of five string presets — llama3, mistral, chatml, phi, gemma — or as an object ({custom: '...'} or {modelId: '...'}). A string that is not one of the five preset keys fails the factory lookup and throws, listing the valid keys in the message.

Solutions

  1. Use exactly one of: llama3, mistral, chatml, phi, gemma
  2. For arbitrary models, pass an object instead: {modelId: 'meta-llama/Llama-3.1-8B-Instruct'} (uses detectFromModelId) or {custom: '<your template string>'}
  3. Upgrade @ruvector/ruvllm-wasm if you expect a newer preset vocabulary
  4. Validate template strings at config-load time against the five keys

Example fix

// before
const prompt = await formatChat('llama-3.1', messages); // throws: Unknown template preset
// after
const prompt = await formatChat(
  { modelId: 'meta-llama/Llama-3.1-8B-Instruct' }, // or 'llama3' preset exactly
  messages
);
Defensive patterns

Strategy: type-guard

Validate before calling

const PRESETS = ['llama3', 'mistral', 'chatml', 'phi', 'gemma'] as const;
if (typeof template === 'string' && !PRESETS.includes(template as any)) {
  template = { modelId: template }; // reinterpret a model id instead of failing
}

Type guard

type ChatPreset = 'llama3' | 'mistral' | 'chatml' | 'phi' | 'gemma';
const isChatPreset = (s: string): s is ChatPreset =>
  ['llama3', 'mistral', 'chatml', 'phi', 'gemma'].includes(s);

Try / catch

try {
  text = await formatChat(template, messages);
} catch (e) {
  if (e instanceof Error && e.message.startsWith('Unknown template preset')) {
    text = await formatChat({ modelId: template as string }, messages); // fall back to detectFromModelId
  } else throw e;
}

Prevention

When it happens

Trigger: Passing 'llama-3' or 'Llama3' (wrong casing/hyphen); passing a HuggingFace model id string like 'llama-3.1-8b-instruct' where a preset key is required; using a preset name added in a newer version of the package than the one installed.

Common situations: Config files carrying model names instead of preset names; upgrading models without updating the template field; copy-pasting template names from HF docs that do not match this wrapper's five-key vocabulary.

Related errors


AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18). Data as JSON: /api/errors/5aeee626f3eae8eb. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/ruvector/ruvllm-wasm.ts:347

export async function formatChat(
  messages: ChatMessage[],
  template: TemplatePreset | { custom: string } | { modelId: string },
): Promise<string> {
  await initRuvllmWasm();
  const mod = await import('@ruvector/ruvllm-wasm');

  // Build template
  let tmpl: any;
  if (typeof template === 'string') {
    const presets: Record<string, () => any> = {
      llama3: () => mod.ChatTemplateWasm.llama3(),
      mistral: () => mod.ChatTemplateWasm.mistral(),
      chatml: () => mod.ChatTemplateWasm.chatml(),
      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);

View on GitHub (pinned to fa13ee4ad6)