n8n-io/n8n · error

Metadata key "${CONTENT_KEY}" is reserved for the document c

Error message

Metadata key "${CONTENT_KEY}" is reserved for the document content and cannot be set.

What it means

Thrown by toPineconeMetadata during upsert when the caller's metadata object already contains the reserved key `_content` (CONTENT_KEY). Pinecone has no dedicated content column, so document content is stored under `_content` alongside the caller's metadata; letting a caller set it would overwrite or shadow the real content. The check runs before any Pinecone request is sent.

Source

Thrown at packages/@n8n/agents/src/vector-stores/pinecone.ts:151

	for (const item of items) {
		const itemBytes = Buffer.byteLength(JSON.stringify(item));
		if (batch.length > 0 && (batch.length >= maxCount || batchBytes + itemBytes > maxBytes)) {
			batches.push(batch);
			batch = [];
			batchBytes = 0;
		}
		batch.push(item);
		batchBytes += itemBytes;
	}
	if (batch.length > 0) batches.push(batch);

	return batches;
}

function toPineconeMetadata(content: string, metadata: JSONObject): RecordMetadata {
	if (CONTENT_KEY in metadata) {
		throw new Error(
			`Metadata key "${CONTENT_KEY}" is reserved for the document content and cannot be set.`,
		);
	}
	const result: RecordMetadata = { [CONTENT_KEY]: content };
	for (const [key, value] of Object.entries(metadata)) {
		assertValidMetadataValue(key, value);
		result[key] = value;
	}
	return result;
}

/** Pinecone metadata values are flat: string, number, boolean, or an array of strings — no nested objects or null. */
function assertValidMetadataValue(
	key: string,
	value: JSONValue | undefined,
): asserts value is string | number | boolean | string[] {
	if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
		return;

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Rename the `_content` metadata key in your records to something else (e.g. `content_summary`, `body`).
  2. If the value really is the document content, set it as `doc.content` (the VectorDocument.content field) instead of in metadata.
  3. Strip `_content` from metadata during your ETL/import step before calling addDocuments.

Example fix

// before
await store.addDocuments([{
  content: 'Real body text',
  metadata: { _content: 'collision', topic: 'billing' },
}]);

// after
await store.addDocuments([{
  content: 'Real body text',
  metadata: { topic: 'billing' },
}]);
Defensive patterns

Strategy: validation

Validate before calling

const RESERVED = '_content';

function sanitizeMetadata(metadata: Record<string, unknown>): Record<string, unknown> {
  if (RESERVED in metadata) {
    throw new Error(`Metadata key "${RESERVED}" is reserved; rename or drop it`);
  }
  return metadata;
}

await store.addDocuments(docs.map((d) => ({ ...d, metadata: sanitizeMetadata(d.metadata ?? {}) })));

Type guard

function isSafeMetadataKey(key: string): boolean {
  return key !== '_content';
}

Try / catch

// Strip the reserved key before upsert if it might be present:
const clean = (m: Record<string, unknown>) => {
  const { _content, ...rest } = m as Record<string, unknown>;
  return rest;
};
await store.addDocuments(docs.map((d) => ({ ...d, metadata: clean(d.metadata ?? {}) })));

Prevention

When it happens

Trigger: Upserting a VectorDocument whose metadata is `{ _content: '...', topic: 'x' }`; importing records from another system that already used `_content` as a metadata field; an ETL pipeline that prefixes all fields with underscore.

Common situations: Migrating from a schema where `_content` was a normal field; auto-prefixing metadata keys; field-name collision from a third-party data feed; re-upserting records previously enriched with their own `_content` metadata.

Related errors


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