{"record":{"id":"edb081485919b5cf","repo":"n8n-io/n8n","slug":"thread-ref-threadid-the-live-turn-is-the-first","errorCode":null,"errorMessage":"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.","messagePattern":"Thread (.+?): the live turn is the first/only user turn — no prior turn to seed\\. Pin a later turn, or use a plain conversation case\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/evaluations/harness/langsmith-seed.ts","lineNumber":388,"sourceCode":"\t// Split point: the live turn is sent live; everything strictly before it is the\n\t// seed. Default = the last user turn; a `liveTurnRunId` pin (LangTracer's live-turn\n\t// picker) moves it earlier, discarding the real turns at/after it.\n\tconst userTurns = rootRuns.filter((r) => userMessageOf(r) !== undefined);\n\tlet liveIndex = userTurns.length - 1; // default: last user turn (unchanged behavior)\n\tif (ref.liveTurnRunId) {\n\t\tconst idx = userTurns.findIndex((r) => r.id === ref.liveTurnRunId);\n\t\tif (idx === -1) {\n\t\t\tconsole.warn(\n\t\t\t\t`[seed] Thread ${ref.threadId}: pinned liveTurnRunId ${ref.liveTurnRunId} not found among ` +\n\t\t\t\t\t`${userTurns.length} user turn(s) — falling back to the last user turn. (LangTracer pins the ` +\n\t\t\t\t\t\"agent_role=message_turn root id; verify it matches the name==='turn' root id.)\",\n\t\t\t);\n\t\t} else {\n\t\t\tliveIndex = idx;\n\t\t}\n\t}\n\tif (liveIndex < 1) {\n\t\tthrow new Error(\n\t\t\t`Thread ${ref.threadId}: the live turn is the first/only user turn — no prior turn to seed. ` +\n\t\t\t\t'Pin a later turn, or use a plain conversation case.',\n\t\t);\n\t}\n\tconst liveTurnRun = userTurns[liveIndex];\n\tconst boundaryMs = new Date(liveTurnRun.start_time ?? NaN).getTime();\n\tconst liveTurn = userMessageOf(liveTurnRun)!;\n\n\tconst messages = buildSeedMessages(rootRuns, toolRuns, boundaryMs);\n\tif (messages.length === 0) {\n\t\tthrow new Error(\n\t\t\t`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).`,\n\t\t);\n\t}\n\tconst sdkVersion = rootRuns\n\t\t.map((r) => asString(metadata(r).workflow_sdk_version))\n\t\t.find((v) => v !== undefined);\n\tconst workflows = buildSeedWorkflows(workflowScanRuns, boundaryMs, ref.threadId, sdkVersion);","sourceCodeStart":370,"sourceCodeEnd":406,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/evaluations/harness/langsmith-seed.ts#L370-L406","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pin liveTurnRunId to a later user turn (index >= 1) so prior turns exist to seed.","If the case is intentionally single-turn, switch it to a plain conversation case (no seed reference) instead of a seeded one.","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.","Re-record the underlying thread to capture additional turns before the live one."],"exampleFix":"// before — case.json pins the only user turn\n{ \"seed\": { \"kind\": \"langsmith\", \"threadId\": \"t1\", \"liveTurnRunId\": \"<first-turn-id>\" } }\n\n// after — pin a later turn, or drop the seed for a plain case\n{ \"seed\": { \"kind\": \"langsmith\", \"threadId\": \"t1\", \"liveTurnRunId\": \"<second-turn-id>\" } }\n// or remove the `seed` block entirely for a plain conversation case","handlingStrategy":"validation","validationCode":"function canSeedLiveTurn(userTurns: { id: string }[], liveTurnRunId?: string): boolean {\n  const idx = liveTurnRunId\n    ? userTurns.findIndex((r) => r.id === liveTurnRunId)\n    : userTurns.length - 1;\n  return idx > 0; // need at least one prior turn\n}\n\nif (!canSeedLiveTurn(userTurns, ref.liveTurnRunId)) {\n  throw new Error('cannot seed: live turn is the first/only turn; use a plain case instead');\n}","typeGuard":"null","tryCatchPattern":"try {\n  buildSeedFromLangsmith(client, ref, sourceProject, logger);\n} catch (e) {\n  if (e instanceof Error && /no prior turn to seed/.test(e.message)) {\n    // downgrade this case to a plain conversation case, or pick a later turn\n    return asPlainCase(ref);\n  }\n  throw e;\n}","preventionTips":["When authoring a case, assert the pinned liveTurnRunId is not the first user turn.","Use LangTracer's pinned root id and double-check it resolves to index >= 1.","For single-turn scenarios, prefer plain conversation cases over seeded ones."],"tags":["eval-harness","seed","conversation-case","configuration"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}