n8n-io/n8n · error · Error

Thread ${ref.threadId} reconstructed to zero seed messages b

Error message

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).

What it means

Thrown after buildSeedMessages returns an empty array despite prior turns existing. It means the LangSmith trace's structure does not match what the reconstruction expects: root runs named 'turn' carrying inputs.message and outputs.response. The harness prefers failing loudly over silently seeding an empty conversation.

Source

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

					"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);
	const dataTables = buildSeedDataTables(toolRuns, boundaryMs);

	return {
		seed: {
			source: { kind: 'langsmith', threadId: ref.threadId, sourceProject },
			messages,
			workflows,
			dataTables,
			// A trace carries no agent artifacts yet; only authored seeds can seed one.
			agents: [],
		},

View on GitHub (pinned to 5ac6606e81)

Solutions

  1. Inspect the trace: list root runs for the thread and check their `name` field and inputs/outputs keys against the expected 'turn' / inputs.message / outputs.response shape.
  2. If the shape drifted, update buildSeedMessages to match the new run naming and I/O keys.
  3. Re-record the trace with the current SDK so the shape matches the harness's expectations.
  4. Confirm userMessageOf and response extraction helpers still find their fields on the new run shape.

Example fix

// before — buildSeedMessages expects runs named 'turn' with inputs.message
const messages = buildSeedMessages(rootRuns, toolRuns, boundaryMs);

// after — log the actual shape, then align the helper
console.dir(rootRuns.map(r => ({ name: r.name, inputs: Object.keys(r.inputs ?? {}), outputs: Object.keys(r.outputs ?? {}) })));
// e.g. update buildSeedMessages to filter `r.name === 'message_turn'` if that is the new name
Defensive patterns

Strategy: validation

Validate before calling

function traceShapeLooksExpected(rootRuns: { name?: string; inputs?: unknown; outputs?: unknown }[]): boolean {
  if (!rootRuns.length) return false;
  return rootRuns.every((r) =>
    r.name === 'turn' &&
    !!r.inputs && typeof r.inputs === 'object' && 'message' in (r.inputs as object) &&
    !!r.outputs && typeof r.outputs === 'object' && 'response' in (r.outputs as object),
  );
}

if (!traceShapeLooksExpected(rootRuns)) {
  throw new Error('trace shape does not match reconstruction expectations; inspect rootRuns');
}

Type guard

null

Try / catch

try {
  const result = buildSeedFromLangsmith(client, ref, sourceProject, logger);
} catch (e) {
  if (e instanceof Error && /reconstructed to zero seed messages/.test(e.message)) {
    logger.error('trace shape drift detected', { rootRunsSummary: rootRuns.map(r => r.name) });
    // re-record the trace or update buildSeedMessages
  }
  throw e;
}

Prevention

When it happens

Trigger: Root runs in the trace are no longer named 'turn'; inputs.message / outputs.response keys have been renamed by an SDK change; the trace was recorded by a different SDK version with a different run shape; nested/parent_run_id structure changed so root filtering yields nothing.

Common situations: Upgrading @n8n/workflow-sdk or LangTracer changed run naming conventions; replaying a trace recorded by an older agent runtime; LangSmith run enrichment altered the payload shape.

Related errors


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