n8n-io/n8n · error · Error

topK must be an integer >= 1, got ${k}

Error message

topK must be an integer >= 1, got ${k}

What it means

Thrown by assertValidTopK() whenever a topK value is not a positive integer. Validation runs both eagerly in the .topK(k) builder and again on the per-call opts.topK passed to search(). The check is Number.isInteger(k) && k >= 1, so fractional numbers, zero, negatives, NaN, and Infinity all fail. This protects the underlying LIMIT/topK passed to each backend.

Source

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

			throw new Error(
				`VectorStore "${this.name}" requires an embedding model — set it via .embeddingModel()`,
			);
		}
		return { backend: this.backend, embeddingModel: this.embeddingModelValue };
	}

	/** Normalizes and validates a filter; returns `undefined` for an empty one so it's never a no-op `WHERE`. */
	private resolveFilter(input?: VectorFilterInput): VectorFilter | undefined {
		if (input === undefined) return undefined;
		const normalized = normalizeFilterInput(input);
		assertValidFilter(normalized);
		return normalized.conditions.length > 0 ? normalized : undefined;
	}
}

function assertValidTopK(k: number): void {
	if (!Number.isInteger(k) || k < 1) {
		throw new Error(`topK must be an integer >= 1, got ${k}`);
	}
}

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Pass a positive integer literal or a value you have clamped: `Math.max(1, Math.floor(value))`.
  2. If 0/'unset' must be supported in your config layer, translate it to the default (4) or omit the topK option entirely so the builder default applies.
  3. Validate external topK input with a guard before calling .topK() or search().

Example fix

// before
store.topK(0);
await store.search('q', { topK: 2.5 });

// after
store.topK(5);
await store.search('q', { topK: Math.max(1, Math.floor(config.limit)) });
Defensive patterns

Strategy: validation

Validate before calling

function safeTopK(value: unknown): number {
  const n = typeof value === 'number' ? value : Number(value);
  if (!Number.isInteger(n) || n < 1) {
    throw new Error(`Invalid topK ${String(value)}: must be an integer >= 1`);
  }
  return n;
}

// Usage:
store.topK(safeTopK(config.limit));
await store.search('q', { topK: safeTopK(req.body.limit) });

Type guard

function isValidTopK(value: unknown): value is number {
  return typeof value === 'number' && Number.isInteger(value) && value >= 1;
}

if (isValidTopK(input)) {
  store.topK(input);
}

Try / catch

if (opts?.topK !== undefined && (!Number.isInteger(opts.topK) || opts.topK < 1)) {
  return res.status(400).json({ error: 'topK must be an integer >= 1' });
}
await store.search(query, opts);

Prevention

When it happens

Trigger: Calling `store.topK(0)`, `store.topK(2.5)`, `store.topK(-1)`, or `store.topK(NaN)`; calling `store.search('q', { topK: Math.round(score) })` where score rounds to 0; reading topK from user/config input parsed as a float; spreading a default of 0 that was meant as 'unset'.

Common situations: Config/UI slider that allows 0 as 'no limit'; JSON config with topK: 0 mistaken for 'use default'; arithmetic that produces a fractional result (e.g. count/2); coerced string input `Number('abc')` yielding NaN.

Related errors


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