{"record":{"id":"7e12a308da40d826","repo":"n8n-io/n8n","slug":"scenario-scenario-name-declares-seed-table","errorCode":null,"errorMessage":"Scenario \"${scenario.name}\" declares seed table \"${table.name}\" that was not pre-seeded before the build; cannot bind its rows.","messagePattern":"Scenario \"(.+?)\" declares seed table \"(.+?)\" that was not pre-seeded before the build; cannot bind its rows\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/evaluations/harness/seed-tables.ts","lineNumber":174,"sourceCode":" * ids, just before that scenario executes (TRUST-311). Clears whatever rows a\n * prior scenario — or a build-time self-verification execution — left, then\n * inserts this scenario's declared rows, so each scenario runs against exactly\n * the state it declared (and scenarios may carry different rows for the same\n * table). `tableIdsByName` maps the declared table name to the real id created\n * before the build turn; a name missing from it means the table was never\n * pre-seeded, which is a harness bug, so throw rather than silently skip.\n */\nexport async function reseedScenarioTables(\n\tclient: N8nClient,\n\tscenario: ExecutionScenario,\n\tthreadId: string,\n\ttableIdsByName: Record<string, string>,\n\tlogger: EvalLogger,\n): Promise<void> {\n\tfor (const table of scenario.seedDataTables ?? []) {\n\t\tconst tableId = tableIdsByName[table.name];\n\t\tif (!tableId) {\n\t\t\tthrow new Error(\n\t\t\t\t`Scenario \"${scenario.name}\" declares seed table \"${table.name}\" that was not pre-seeded before the build; cannot bind its rows.`,\n\t\t\t);\n\t\t}\n\t\tawait client.seedDataTableRows(threadId, tableId, table.rows ?? []);\n\t\tlogger.verbose(\n\t\t\t`    [${scenario.name}] reseeded data table \"${table.name}\" (${String((table.rows ?? []).length)} row(s))`,\n\t\t);\n\t}\n}\n\n/** Two seed tables bind the same way iff their columns + rows match (the id\n *  differs per declaration and is cosmetic under by-name seeding). */\nfunction sameSeedTableShape(\n\ta: InstanceAiEvalSeedDataTable,\n\tb: InstanceAiEvalSeedDataTable,\n): boolean {\n\treturn (\n\t\tJSON.stringify({ columns: a.columns, rows: a.rows }) ===","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/evaluations/harness/seed-tables.ts#L156-L192","documentation":"Thrown by reseedScenarioTables when a scenario declares a seed data table whose name has no entry in the tableIdsByName map produced by the pre-seed step. The harness pre-seeds all declared tables (creating them in the workspace) before the build, then reseeds per-scenario rows by name; a missing mapping means the table was never created, so rows cannot be bound. This is treated as a harness bug rather than user error.","triggerScenarios":"The pre-seed step skipped a table name (e.g. due to a dedup collision where a different name won); the tableIdsByName map was built from a different scenario list than the one being reseeded; a race where the table creation failed silently upstream.","commonSituations":"Case authoring introduced a scenario with a new table name after the pre-seed map was computed; refactoring seed-table collection changed which names are pre-seeded; parallel scenario setup where one path dropped a table.","solutions":["Ensure every scenario.seedDataTables[].name is included in the pre-seed pass that builds tableIdsByName (verify the same scenario list is used for both steps).","Check upstream pre-seed logs for a skipped/failed creation of the named table.","Confirm the name strings match exactly (case, whitespace) between the scenario declaration and the pre-seed map keys."],"exampleFix":"// before — pre-seed only creates some names, reseed expects all\nconst tableIdsByName = preSeedTables(scenarioA.seedDataTables); // missing scenarioB's table\nreseedScenarioTables(client, scenarioB, threadId, tableIdsByName, logger);\n\n// after — pre-seed from the union of all scenarios\nconst allTables = [...scenarioA.seedDataTables, ...scenarioB.seedDataTables];\nconst tableIdsByName = preSeedTables(allTables);\nreseedScenarioTables(client, scenarioB, threadId, tableIdsByName, logger);","handlingStrategy":"validation","validationCode":"function allScenarioTablesPreSeeded(\n  scenarios: { name: string; seedDataTables?: { name: string }[] }[],\n  tableIdsByName: Record<string, string>,\n): { ok: true } | { ok: false; missing: { scenario: string; table: string }[] } {\n  const missing: { scenario: string; table: string }[] = [];\n  for (const s of scenarios)\n    for (const t of s.seedDataTables ?? [])\n      if (!tableIdsByName[t.name]) missing.push({ scenario: s.name, table: t.name });\n  return missing.length ? { ok: false, missing } : { ok: true };\n}\n\nconst check = allScenarioTablesPreSeeded(allScenarios, tableIdsByName);\nif (!check.ok) throw new Error(`pre-seed missing: ${JSON.stringify(check.missing)}`);","typeGuard":"null","tryCatchPattern":"try {\n  await reseedScenarioTables(client, scenario, threadId, tableIdsByName, logger);\n} catch (e) {\n  if (e instanceof Error && /was not pre-seeded before the build/.test(e.message)) {\n    // re-run pre-seed for the union of all scenario tables, then retry\n  }\n  throw e;\n}","preventionTips":["Derive tableIdsByName from the union of all scenario tables, not a subset.","Add a unit test asserting pre-seed and reseed see the same scenario list.","Log pre-seed table-name set alongside reseed declarations for diffing."],"tags":["eval-harness","seed-tables","harness-bug","data-integrity"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}