n8n-io/n8n · error · Error

Restore was asked to seed ${String(agents.length)} agent(s)

Error message

Restore was asked to seed ${String(agents.length)} agent(s) but the response carried ${String(restored.agentIds.length)} — the backend likely predates agent seeding.

What it means

restoreThread sends agents[] in the body and expects the response's agentIds to match in length. Because agentIds defaults to [] for backends that predate agent seeding, a mismatch would otherwise look like 'restored fine, zero agents'. The guard insists that if you asked for agents, you got them.

Source

Thrown at packages/@n8n/instance-ai/evaluations/clients/n8n-client.ts:795

	): Promise<{
		restored: number;
		workflowIds: string[];
		dataTableIds: string[];
		agentIds: string[];
	}> {
		const body: Record<string, unknown> = { threadId, messages, workflows, dataTables, agents };
		if (options.uniquifyNames !== undefined) body.uniquifyNames = options.uniquifyNames;
		const result = await this.fetch('/rest/instance-ai/eval/restore-thread', {
			method: 'POST',
			body,
			timeoutMs: RESTORE_THREAD_TIMEOUT_MS,
		});
		const restored = RestoreThreadEnvelope.parse(result).data;
		// `agentIds` defaults to [] for backends that predate agent seeding, which
		// would read as "restored fine, zero agents" on a backend that just ignored
		// the field. If we asked for agents, insist they came back.
		if (agents.length > 0 && restored.agentIds.length !== agents.length) {
			throw new Error(
				`Restore was asked to seed ${String(agents.length)} agent(s) but the response carried ${String(restored.agentIds.length)} — the backend likely predates agent seeding.`,
			);
		}
		return restored;
	}

	/**
	 * Reset an existing data table's rows to exactly `rows` (clear-then-insert),
	 * for the per-scenario row seeding of a case that pre-created its tables
	 * before the build turn (TRUST-311). Unlike `restoreThread` (which CREATES
	 * tables), this targets a table that already exists by id, so a scenario can
	 * declare its own row state without disturbing the table the built workflow
	 * bound. `threadId` scopes the table to the run's project server-side.
	 * POST /rest/instance-ai/eval/seed-data-table-rows
	 */
	async seedDataTableRows(
		threadId: string,
		tableId: string,

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Upgrade the n8n instance to a version that supports agent seeding in restore-thread.
  2. Inspect the backend response to see which agents were not seeded and why.
  3. If running against an older backend, remove agents from the restore payload temporarily.
Defensive patterns

Strategy: validation

Validate before calling

// Pre-check backend capability before sending agents:
const caps = await client.unwrapRestData<{ agentSeeding?: boolean }>(
  await client.fetch('/rest/instance-ai/eval/capabilities'));
if (agents.length > 0 && !caps.agentSeeding)
  throw new Error('backend does not support agent seeding; upgrade or omit agents');

Type guard

const hasAgentIds = (v: unknown): v is { agentIds: unknown[] } =>
  typeof v === 'object' && v !== null && Array.isArray((v as any).agentIds);

Try / catch

try { await client.restoreThread({ ...body, agents }); }
catch (e) {
  if (e instanceof Error && e.message.includes('predates agent seeding')) { /* drop agents or upgrade */ }
  else throw e;
}

Prevention

When it happens

Trigger: Running eval restore against an n8n instance older than the agent-seeding feature; partial seeding where some agents failed to create silently.

Common situations: Version skew between eval client and n8n backend; an agent definition referencing a missing resource that the backend dropped without error.

Related errors


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