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
- 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.
- Disable batch capability (make the embedder non-batch) so the engine falls back to parallel single-text calls, isolating per-item failures.
- 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
- Unit-test custom embedders with N distinct inputs and assert embeddings.length === N and order preservation.
- Never swallow per-item embed errors inside batch loops; propagate or serialize them.
- Prefer the engine's parallel single-text fallback (non-batch embedder) unless batch behavior is well tested.
- Watch provider rate limits that cause partial batch responses and add retries at the provider client level.
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
- [FilesystemStorage] duplicate file path: ${file.path}
- Platform connection response missing DATABASE_URL.
- AGENT_GENERATE_MALFORMED_RESULT
- OBSERVABILITY_STORAGE_GET_SPANS_NOT_IMPLEMENTED
- OBSERVABILITY_STORAGE_BATCH_CREATE_SPAN_NOT_IMPLEMENTED
AI-assisted analysis of mastra-ai/mastra@75dd419e61 (2026-08-30).
Data as JSON: /api/errors/ead51e4d245fc73e.
Report an issue: GitHub.