ruvnet/ruflo · error · Error

Invalid embedding model name

Error message

Invalid embedding model name: ${embeddingModel}

What it means

Thrown by `claude-flow init --with-embeddings` when --embedding-model does not match ^[a-zA-Z0-9_-]+/[a-zA-Z0-9._-]+$. The string is later passed to an npx invocation (in some configurations via cmd.exe on Windows), so it is restricted to a strict Hugging Face-style org/model identifier as CRIT-02 command-injection hardening. Validation happens before any child process spawns.

Solutions

  1. Omit the flag — the default Xenova/all-MiniLM-L6-v2 always passes validation
  2. Pass a plain org/model id, e.g. --embedding-model Xenova/all-MiniLM-L6-v2 or BAAI/bge-small-en-v1.5
  3. Strip @revision and :tag suffixes from the identifier before passing it

Example fix

# before
claude-flow init --with-embeddings --embedding-model minilm-l6

# after
claude-flow init --with-embeddings --embedding-model Xenova/all-MiniLM-L6-v2
Defensive patterns

Strategy: validation

Validate before calling

const EMBEDDING_MODEL_RE = /^[a-zA-Z0-9_-]+\/[a-zA-Z0-9._-]+$/;
if (!EMBEDDING_MODEL_RE.test(embeddingModel)) {
  throw new Error(`Bad model id: ${embeddingModel} (expected org/model)`);
}
await cli.init({ withEmbeddings: true, embeddingModel });

Type guard

function isValidEmbeddingModelId(v: unknown): v is string {
  return typeof v === 'string' && /^[a-zA-Z0-9_-]+\/[a-zA-Z0-9._-]+$/.test(v);
}

Prevention

When it happens

Trigger: Passing --embedding-model all-MiniLM-L6-v2 (missing org), Xenova/all-MiniLM-L6-v2@refs/pr/2 (@ not allowed), 'Xenova / model' (spaces), or org/model:revision (colon rejected), together with --with-embeddings on init.

Common situations: Using Docker-style tag syntax (model:latest), pasting a Hugging Face URL instead of the repo id, or copying a model@revision pin from docs into the flag.

Related errors


AI-assisted analysis of ruvnet/ruflo@5234333c34 (2026-08-18). Data as JSON: /api/errors/48e2a09a8ec70909. Report an issue: GitHub.

Appendix: source

Thrown at v3/@claude-flow/cli/src/commands/init.ts:835

      }

      output.writeln();
      output.printSuccess('All services started');
    }

    // Handle --with-embeddings
    const withEmbeddings = ctx.flags['with-embeddings'] || ctx.flags.withEmbeddings;
    const embeddingModel = (ctx.flags['embedding-model'] || ctx.flags.embeddingModel || 'Xenova/all-MiniLM-L6-v2') as string;

    if (withEmbeddings) {
      output.writeln();
      output.printInfo('Initializing ONNX embedding subsystem...');

      const { execFileSync: execFileInit } = await import('child_process');

      // Validate embeddingModel: must match pattern org/model-name (CRIT-02)
      if (!/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9._-]+$/.test(embeddingModel)) {
        throw new Error(`Invalid embedding model name: ${embeddingModel}`);
      }

      try {
        output.writeln(output.dim(`  Model: ${embeddingModel}`));
        output.writeln(output.dim('  Hyperbolic: Enabled (Poincaré ball)'));
        // #2770: On Windows, `npx` ships as `npx.cmd`; execFileSync cannot spawn
        // a .cmd file without going through cmd.exe. Enable shell on win32 so
        // cmd.exe resolves the .cmd extension. POSIX keeps shell:false.
        // NOTE: shell:true joins args by spaces and passes to cmd.exe — the args
        // here are hard-coded flags + an npm package name pre-validated against
        // /^[a-zA-Z0-9_-]+\/[a-zA-Z0-9._-]+$/, so no injection risk. If
        // user-controlled args are ever added, escape them before spawn.
        execFileInit('npx', [
          '@claude-flow/cli@latest', 'embeddings', 'init',
          '--model', embeddingModel,
          '--no-download', '--force',
        ], {
          stdio: 'pipe',

View on GitHub (pinned to 5234333c34)