n8n-io/n8n · error

Invalid Qdrant point id "${id}": Qdrant requires ids to be a

Error message

Invalid Qdrant point id "${id}": Qdrant requires ids to be a UUID or a canonical unsigned integer.

What it means

Thrown by toPointId in the Qdrant backend when a record id is neither a UUID (hyphenated, simple, urn, or braced forms accepted, matching Qdrant's Rust parser) nor a canonical unsigned integer. Qdrant rejects arbitrary string ids with a 400, so this validates first and surfaces a clear message. Note: VectorStore.addDocuments generates UUIDs via crypto.randomUUID() by default, so this only fires when the caller supplies a custom doc.id.

Source

Thrown at packages/@n8n/agents/src/vector-stores/qdrant.ts:131

		if (!this.client) {
			const { QdrantClient: QdrantClientCtor } = await import('@qdrant/js-client-rest');
			this.client = new QdrantClientCtor({
				url: this.constructorOptions.url,
				apiKey: this.constructorOptions.apiKey,
			});
		}
		return this.client;
	}
}

/** Qdrant only accepts UUID or unsigned-integer point ids. */
function toPointId(id: string): string | number {
	if (UUID_PATTERN.test(id)) return id;
	const numeric = Number(id);
	if (UNSIGNED_INT_PATTERN.test(id) && Number.isSafeInteger(numeric) && String(numeric) === id) {
		return numeric;
	}
	throw new Error(
		`Invalid Qdrant point id "${id}": Qdrant requires ids to be a UUID or a canonical unsigned integer.`,
	);
}

function toQueryResult(point: Schemas['ScoredPoint']): VectorQueryResult {
	const payload = (point.payload ?? {}) as unknown as QdrantPayload;
	return {
		id: String(point.id),
		content: payload.content,
		metadata: payload.metadata ?? {},
		score: point.score,
	};
}

/** Negations are expressed as nested `must_not` filters so both `and`/`or` combinators work uniformly. */
function buildQdrantFilter(filter: VectorFilter): Schemas['Filter'] {
	const conditions = filter.conditions.map(buildCondition);
	return filter.combineWith === 'or' ? { should: conditions } : { must: conditions };

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Let VectorStore.addDocuments auto-generate UUIDs (omit doc.id) — recommended for Qdrant.
  2. If you need stable ids, generate and store UUIDs (crypto.randomUUID()) and use those as the canonical id everywhere.
  3. For numeric ids, pass a canonical unsigned integer string with no leading zeros or sign (e.g. '5', not '05' or '+5').
  4. If you must keep opaque string ids, choose a different backend (PgVectorStore/SupabaseVectorStore accept arbitrary text ids).

Example fix

// before — Qdrant rejects the custom slug id
await store.addDocuments([{ id: 'user_123', content: 'doc', metadata: {} }]);

// after — let the store mint UUIDs, keep your slug in metadata
await store.addDocuments([{ content: 'doc', metadata: { slug: 'user_123' } }]);
Defensive patterns

Strategy: validation

Validate before calling

const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
const UINT = /^\d+$/;

function isQdrantSafeId(id: string): boolean {
  if (UUID.test(id)) return true;
  const n = Number(id);
  return UINT.test(id) && Number.isSafeInteger(n) && String(n) === id;
}

if (doc.id && !isQdrantSafeId(doc.id)) {
  throw new Error(`Refusing id "${doc.id}" for Qdrant; use a UUID or unsigned int`);
}

Type guard

function isQdrantId(id: string): boolean {
  return isQdrantSafeId(id); // see validationCode
}

Prevention

When it happens

Trigger: Upserting a VectorDocument with `id: 'user_123'`, `id: 'doc:abc'`, `id: '-5'`, `id: '3.0'` (non-canonical), or `id: '0123'` (leading zero — fails the `String(numeric) === id` canonical check); deleting by such ids.

Common situations: Using business-domain string ids (slugs, SKUs, composite keys) instead of UUIDs; migrating ids from another store; integer ids with leading zeros or signs; truncating/padding ids.

Related errors


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