n8n-io/n8n · error · Error

Thread ${ref.threadId}: the live turn is the first/only user

Error message

Thread ${ref.threadId}: the live turn is the first/only user turn — no prior turn to seed. Pin a later turn, or use a plain conversation case.

What it means

Thrown when seeding a conversation case whose pinned live turn is the first (index 0) or only user turn — there are no prior turns to reconstruct as seed messages, so the seeded conversation would be empty. The harness needs at least one earlier user turn to build a meaningful pre-live context.

Source

Thrown at packages/@n8n/instance-ai/evaluations/harness/langsmith-seed.ts:388

	// Split point: the live turn is sent live; everything strictly before it is the
	// seed. Default = the last user turn; a `liveTurnRunId` pin (LangTracer's live-turn
	// picker) moves it earlier, discarding the real turns at/after it.
	const userTurns = rootRuns.filter((r) => userMessageOf(r) !== undefined);
	let liveIndex = userTurns.length - 1; // default: last user turn (unchanged behavior)
	if (ref.liveTurnRunId) {
		const idx = userTurns.findIndex((r) => r.id === ref.liveTurnRunId);
		if (idx === -1) {
			console.warn(
				`[seed] Thread ${ref.threadId}: pinned liveTurnRunId ${ref.liveTurnRunId} not found among ` +
					`${userTurns.length} user turn(s) — falling back to the last user turn. (LangTracer pins the ` +
					"agent_role=message_turn root id; verify it matches the name==='turn' root id.)",
			);
		} else {
			liveIndex = idx;
		}
	}
	if (liveIndex < 1) {
		throw new Error(
			`Thread ${ref.threadId}: the live turn is the first/only user turn — no prior turn to seed. ` +
				'Pin a later turn, or use a plain conversation case.',
		);
	}
	const liveTurnRun = userTurns[liveIndex];
	const boundaryMs = new Date(liveTurnRun.start_time ?? NaN).getTime();
	const liveTurn = userMessageOf(liveTurnRun)!;

	const messages = buildSeedMessages(rootRuns, toolRuns, boundaryMs);
	if (messages.length === 0) {
		throw new Error(
			`Thread ${ref.threadId} reconstructed to zero seed messages before the live turn — the trace shape may have drifted (expected root runs named 'turn' with inputs.message / outputs.response).`,
		);
	}
	const sdkVersion = rootRuns
		.map((r) => asString(metadata(r).workflow_sdk_version))
		.find((v) => v !== undefined);
	const workflows = buildSeedWorkflows(workflowScanRuns, boundaryMs, ref.threadId, sdkVersion);

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Pin liveTurnRunId to a later user turn (index >= 1) so prior turns exist to seed.
  2. If the case is intentionally single-turn, switch it to a plain conversation case (no seed reference) instead of a seeded one.
  3. Verify the liveTurnRunId matches the LangTracer-pinned agent_role=message_turn root id, since a wrong pin falls back to the last turn which may also be the first.
  4. Re-record the underlying thread to capture additional turns before the live one.

Example fix

// before — case.json pins the only user turn
{ "seed": { "kind": "langsmith", "threadId": "t1", "liveTurnRunId": "<first-turn-id>" } }

// after — pin a later turn, or drop the seed for a plain case
{ "seed": { "kind": "langsmith", "threadId": "t1", "liveTurnRunId": "<second-turn-id>" } }
// or remove the `seed` block entirely for a plain conversation case
Defensive patterns

Strategy: validation

Validate before calling

function canSeedLiveTurn(userTurns: { id: string }[], liveTurnRunId?: string): boolean {
  const idx = liveTurnRunId
    ? userTurns.findIndex((r) => r.id === liveTurnRunId)
    : userTurns.length - 1;
  return idx > 0; // need at least one prior turn
}

if (!canSeedLiveTurn(userTurns, ref.liveTurnRunId)) {
  throw new Error('cannot seed: live turn is the first/only turn; use a plain case instead');
}

Type guard

null

Try / catch

try {
  buildSeedFromLangsmith(client, ref, sourceProject, logger);
} catch (e) {
  if (e instanceof Error && /no prior turn to seed/.test(e.message)) {
    // downgrade this case to a plain conversation case, or pick a later turn
    return asPlainCase(ref);
  }
  throw e;
}

Prevention

When it happens

Trigger: A conversation case with exactly one user turn where liveTurnRunId points at that turn; liveTurnRunId pinned to the very first of several turns (liveIndex === 0); a case that should have been authored as a plain (non-seeded) conversation case but was misclassified.

Common situations: Authoring a seeded eval case from a thread where the interesting behaviour is in turn 1 itself; LangTracer pinning a root id that resolves to index 0; converting a single-shot Q&A into a seeded case by mistake.

Related errors


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