mastra-ai/mastra · critical

Batch embedder returned ${embeddings.length} embeddings for

Error message

Batch embedder returned ${embeddings.length} embeddings for ${docs.length} inputs.

What it means

After batch embedding, SearchEngine asserts the embedder returned exactly one embedding per input document. A count mismatch means the batch embedder implementation violated the embed-many contract (isBatchEmbedder claimed batch capability but returned a misaligned result). The engine throws rather than silently upserting documents with wrong/missing vectors.

Source

Thrown at packages/core/src/workspace/search/search-engine.ts:814

      });
    }
  }

  /**
   * Embed one group of documents with a single embedder call, then write the vectors using
   * upserts no larger than {@link MAX_VECTORS_PER_UPSERT}.
   *
   * Vectors are paired with their documents positionally, so the embedder must return exactly
   * one embedding per input in input order.
   */
  async #embedAndUpsertGroup(docs: IndexDocument[]): Promise<void> {
    if (!this.#vectorConfig || docs.length === 0) return;

    const { vectorStore, indexName } = this.#vectorConfig;

    const embeddings = await this.#embedAll(docs.map(d => d.content));
    if (embeddings.length !== docs.length) {
      throw new Error(`Batch embedder returned ${embeddings.length} embeddings for ${docs.length} inputs.`);
    }

    if (!this.#vectorIndexReady) {
      const dim = embeddings[0]!.length;
      try {
        await vectorStore.createIndex({ indexName, dimension: dim });
      } catch {
        // Already exists, temporarily unavailable, or not required by backend.
      }
    }

    for (let start = 0; start < docs.length; start += MAX_VECTORS_PER_UPSERT) {
      const slice = docs.slice(start, start + MAX_VECTORS_PER_UPSERT);
      await vectorStore.upsert({
        indexName,
        vectors: embeddings.slice(start, start + MAX_VECTORS_PER_UPSERT),
        metadata: slice.map(doc => ({
          id: doc.id,

View on GitHub (pinned to 75dd419e61)

Solutions

  1. Fix the custom batch embedder so it returns exactly one embedding per input, preserving order; on failure, throw or pad/serialize requests instead of dropping items.
  2. Disable batch capability (make the embedder non-batch) so the engine falls back to parallel single-text calls, isolating per-item failures.
  3. Log inputs/outputs at the embedder boundary to find which inputs are being dropped or duplicated, and correct the mapping.

Example fix

// before
async doEmbed({ values }) {
  const out = [];
  for (const v of values) {
    try { out.push(await embedOne(v)); } catch { /* skipped -> mismatch */ }
  }
  return { embeddings: out };
}
// after
async doEmbed({ values }) {
  const embeddings = await Promise.all(values.map(v => embedOne(v))); // throws on failure, 1:1 mapping
  return { embeddings };
}
Defensive patterns

Strategy: try-catch

Validate before calling

function isValidBatchEmbedder(e) {
  return typeof e?.doEmbed === 'function';
}
// smoke-test before indexing: 1:1 output contract
const probe = await embedder.doEmbed({ values: ['a', 'b'] });
if (probe.embeddings.length !== 2) throw new Error('Embedder violates 1:1 batch contract');

Try / catch

try {
  await engine.upsert(docs);
} catch (err) {
  if (err instanceof Error && /Batch embedder returned \d+ embeddings/.test(err.message)) {
    console.error('Embedder 1:1 contract violated; rebuild embedder or switch to non-batch fallback.', err);
    // recreate engine with a compliant or non-batch embedder and retry
  } else throw err;
}

Prevention

When it happens

Trigger: A custom embedder marked as batch-capable returns fewer/more embeddings than inputs (e.g. drops failed items, deduplicates inputs, chunks results incorrectly, or returns a flattened multi-chunk result); an embedder provider silently truncates oversized batches.

Common situations: Writing a custom `doEmbed`/batch embedder wrapper that filters out failed texts; using an embedder that batches internally and returns partial results on API errors; provider returning embeddings per chunk rather than per input.

Related errors


AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30). Data as JSON: /api/errors/ead51e4d245fc73e. Report an issue: GitHub.