{"record":{"id":"e18bca53a5642c2a","repo":"n8n-io/n8n","slug":"thread-ref-threadid-reconstructed-to-zero-seed","errorCode":null,"errorMessage":"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).","messagePattern":"Thread (.+?) 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\\)\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/evaluations/harness/langsmith-seed.ts","lineNumber":399,"sourceCode":"\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);\n\tconst dataTables = buildSeedDataTables(toolRuns, boundaryMs);\n\n\treturn {\n\t\tseed: {\n\t\t\tsource: { kind: 'langsmith', threadId: ref.threadId, sourceProject },\n\t\t\tmessages,\n\t\t\tworkflows,\n\t\t\tdataTables,\n\t\t\t// A trace carries no agent artifacts yet; only authored seeds can seed one.\n\t\t\tagents: [],\n\t\t},","sourceCodeStart":381,"sourceCodeEnd":417,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/evaluations/harness/langsmith-seed.ts#L381-L417","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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.","If the shape drifted, update buildSeedMessages to match the new run naming and I/O keys.","Re-record the trace with the current SDK so the shape matches the harness's expectations.","Confirm userMessageOf and response extraction helpers still find their fields on the new run shape."],"exampleFix":"// before — buildSeedMessages expects runs named 'turn' with inputs.message\nconst messages = buildSeedMessages(rootRuns, toolRuns, boundaryMs);\n\n// after — log the actual shape, then align the helper\nconsole.dir(rootRuns.map(r => ({ name: r.name, inputs: Object.keys(r.inputs ?? {}), outputs: Object.keys(r.outputs ?? {}) })));\n// e.g. update buildSeedMessages to filter `r.name === 'message_turn'` if that is the new name","handlingStrategy":"validation","validationCode":"function traceShapeLooksExpected(rootRuns: { name?: string; inputs?: unknown; outputs?: unknown }[]): boolean {\n  if (!rootRuns.length) return false;\n  return rootRuns.every((r) =>\n    r.name === 'turn' &&\n    !!r.inputs && typeof r.inputs === 'object' && 'message' in (r.inputs as object) &&\n    !!r.outputs && typeof r.outputs === 'object' && 'response' in (r.outputs as object),\n  );\n}\n\nif (!traceShapeLooksExpected(rootRuns)) {\n  throw new Error('trace shape does not match reconstruction expectations; inspect rootRuns');\n}","typeGuard":"null","tryCatchPattern":"try {\n  const result = buildSeedFromLangsmith(client, ref, sourceProject, logger);\n} catch (e) {\n  if (e instanceof Error && /reconstructed to zero seed messages/.test(e.message)) {\n    logger.error('trace shape drift detected', { rootRunsSummary: rootRuns.map(r => r.name) });\n    // re-record the trace or update buildSeedMessages\n  }\n  throw e;\n}","preventionTips":["Pin the @n8n/workflow-sdk version the harness expects in lockfile.","Add a schema assertion over root run shapes before reconstruction.","Re-record eval traces after any SDK upgrade that changes run naming."],"tags":["eval-harness","trace-shape-drift","reconstruction","version-drift"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}