n8n-io/n8n · error · Error

Document at index ${index} has empty content — nothing to em

Error message

Document at index ${index} has empty content — nothing to embed.

What it means

VectorStore.addDocuments() pre-validates every document's content before embedding: content must be a non-empty trimmed string. Embedding empty or whitespace-only content wastes an embedding API call and pollutes the vector index with garbage vectors that match everything and nothing. The check is per-document with the offending index in the message.

Source

Thrown at packages/@n8n/agents/src/sdk/vector-store.ts:94

			assertValidTopK(opts.topK);
		}
		const { backend, embeddingModel } = this.ensureBuilt();
		const { embed } = await import('ai');
		const { embedding } = await embed({ model: embeddingModel, value: query });
		const filter = this.resolveFilter(opts?.filter);
		return await backend.query(embedding, {
			topK: opts?.topK ?? this.topKValue,
			...(filter ? { filter } : {}),
		});
	}

	/** Embed and upsert documents into the store. Returns the ids used (generated when not provided). */
	async addDocuments(docs: VectorDocument[]): Promise<string[]> {
		if (docs.length === 0) return [];

		docs.forEach((doc, index) => {
			if (typeof doc.content !== 'string' || doc.content.trim() === '') {
				throw new Error(`Document at index ${index} has empty content — nothing to embed.`);
			}
		});

		const { backend, embeddingModel } = this.ensureBuilt();
		const ids = docs.map((doc) => doc.id ?? crypto.randomUUID());
		const { embedMany } = await import('ai');
		const { embeddings } = await embedMany({
			model: embeddingModel,
			values: docs.map((doc) => doc.content),
		});
		await backend.upsert(
			docs.map((doc, index) => ({
				id: ids[index],
				vector: embeddings[index],
				content: doc.content,
				metadata: doc.metadata ?? {},
			})),
		);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Filter documents before ingest: docs.filter(d => typeof d.content === 'string' && d.content.trim().length > 0).
  2. Default missing content to a meaningful placeholder or skip the document rather than embedding emptiness.
  3. Validate at the source parser so blank content never reaches addDocuments.

Example fix

// before
store.addDocuments(pages.map(p => ({ content: p.body, metadata: { url: p.url } })));
// throws if any page.body is '' or whitespace

// after — filter empties first
store.addDocuments(
  pages
    .filter(p => typeof p.body === 'string' && p.body.trim().length > 0)
    .map(p => ({ content: p.body, metadata: { url: p.url } })),
);
Defensive patterns

Strategy: validation

Validate before calling

function cleanDocs(docs: VectorDocument[]) {
  return docs.filter(
    d => typeof d.content === 'string' && d.content.trim().length > 0,
  );
}

Type guard

function hasNonEmptyContent(doc: { content: unknown }): doc is { content: string } {
  return typeof doc.content === 'string' && doc.content.trim().length > 0;
}

Try / catch

try {
  await store.addDocuments(docs);
} catch (err) {
  if (err instanceof Error && err.message.includes('empty content')) {
    const cleaned = docs.filter(d => typeof d.content === 'string' && d.content.trim().length > 0);
    if (cleaned.length) await store.addDocuments(cleaned);
  } else throw err;
}

Prevention

When it happens

Trigger: Calling store.addDocuments([{ content: '', metadata: {...} }]) or any document whose content is whitespace (' '), null, undefined, or a non-string. The forEach throws on the first offending index before any embedding work begins.

Common situations: Bulk-ingesting from a source with missing/blank fields (e.g. empty page bodies, filtered-out sections); content that is null after a parsing step; whitespace from stripped HTML; documents built from optional fields that were absent.

Related errors


AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12). Data as JSON: /api/errors/04434e0efdcef1b3. Report an issue: GitHub.