tobi/qmd · error · Error

LLM operations are disabled in CI (set CI=true)

Error message

LLM operations are disabled in CI (set CI=true)

What it means

embedBatch() refuses to run when the LlamaCpp instance is in CI mode (_ciMode). CI mode disables all local LLM operations so that test/CI environments never attempt to load models or run inference.

Solutions

  1. Mock embedBatch in tests instead of calling the real one
  2. Set the env so CI mode is off (e.g. unset CI/NODE_ENV=test) if you genuinely need embeddings locally
  3. Use FTS-only search paths (qmd search) in CI

Example fix

// before
const embs = await llm.embedBatch(texts); // throws in CI
// after
const embs = llm.ciMode ? texts.map(() => null) : await llm.embedBatch(texts);
Defensive patterns

Strategy: try-catch

Validate before calling

if (llm.ciMode) return texts.map(() => null);

Type guard

const canEmbed = (l: LlamaCpp) => !l.ciMode;

Try / catch

try { await llm.embedBatch(texts); } catch (e) { if (/disabled in CI/.test((e as Error).message)) return texts.map(() => null); throw e; }

Prevention

When it happens

Trigger: Calling embedBatch(texts) while the process was constructed with CI mode enabled — e.g. NODE_ENV=test, CI=true env detection at construction time, or a test helper that instantiates LlamaCpp in CI mode.

Common situations: Running unit tests that accidentally call the real embedBatch; CI pipelines where model downloads/inference must be skipped; forgetting to unset CI mode in a local integration test that needs real embeddings.

Related errors


AI-assisted analysis of tobi/qmd@dbfd0b4736 (2026-08-28). Data as JSON: /api/errors/29310b2c13e2e7ea. Report an issue: GitHub.

Appendix: source

Thrown at src/llm.ts:1475

      const embedding = await context.getEmbeddingFor(safeText);

      return {
        embedding: Array.from(embedding.vector),
        model: options.model ?? this.embedModelUri,
      };
    } catch (error) {
      console.error("Embedding error:", error);
      return null;
    }
  }

  /**
   * Batch embed multiple texts efficiently
   * Uses Promise.all for parallel embedding - node-llama-cpp handles batching internally
   */
  async embedBatch(texts: string[], options: EmbedOptions = {}): Promise<(EmbeddingResult | null)[]> {
    if (this._ciMode) throw new Error("LLM operations are disabled in CI (set CI=true)");
    // Ping activity at start to keep models alive during this operation
    this.touchActivity();

    if (texts.length === 0) return [];

    try {
      const contexts = await this.ensureEmbedContexts();
      const n = contexts.length;

      if (n === 1) {
        // Single context: sequential (no point splitting)
        const context = contexts[0]!;
        const embeddings: ({ embedding: number[]; model: string } | null)[] = [];
        for (const text of texts) {
          try {
            const { text: safeText, truncated, limit } = await this.truncateToContextSize(text);
            if (truncated) {
              console.warn(`⚠ Batch text truncated to fit embedding context (${limit} tokens)`);

View on GitHub (pinned to dbfd0b4736)