n8n-io/n8n · error · Error
Scenario "${scenario.name}" declares seed table "${table.nam
Error message
Scenario "${scenario.name}" declares seed table "${table.name}" that was not pre-seeded before the build; cannot bind its rows. What it means
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.
Source
Thrown at packages/@n8n/instance-ai/evaluations/harness/seed-tables.ts:174
* ids, just before that scenario executes (TRUST-311). Clears whatever rows a
* prior scenario — or a build-time self-verification execution — left, then
* inserts this scenario's declared rows, so each scenario runs against exactly
* the state it declared (and scenarios may carry different rows for the same
* table). `tableIdsByName` maps the declared table name to the real id created
* before the build turn; a name missing from it means the table was never
* pre-seeded, which is a harness bug, so throw rather than silently skip.
*/
export async function reseedScenarioTables(
client: N8nClient,
scenario: ExecutionScenario,
threadId: string,
tableIdsByName: Record<string, string>,
logger: EvalLogger,
): Promise<void> {
for (const table of scenario.seedDataTables ?? []) {
const tableId = tableIdsByName[table.name];
if (!tableId) {
throw new Error(
`Scenario "${scenario.name}" declares seed table "${table.name}" that was not pre-seeded before the build; cannot bind its rows.`,
);
}
await client.seedDataTableRows(threadId, tableId, table.rows ?? []);
logger.verbose(
` [${scenario.name}] reseeded data table "${table.name}" (${String((table.rows ?? []).length)} row(s))`,
);
}
}
/** Two seed tables bind the same way iff their columns + rows match (the id
* differs per declaration and is cosmetic under by-name seeding). */
function sameSeedTableShape(
a: InstanceAiEvalSeedDataTable,
b: InstanceAiEvalSeedDataTable,
): boolean {
return (
JSON.stringify({ columns: a.columns, rows: a.rows }) ===View on GitHub (pinned to 5ac6606e81)
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.
Example fix
// before — pre-seed only creates some names, reseed expects all const tableIdsByName = preSeedTables(scenarioA.seedDataTables); // missing scenarioB's table reseedScenarioTables(client, scenarioB, threadId, tableIdsByName, logger); // after — pre-seed from the union of all scenarios const allTables = [...scenarioA.seedDataTables, ...scenarioB.seedDataTables]; const tableIdsByName = preSeedTables(allTables); reseedScenarioTables(client, scenarioB, threadId, tableIdsByName, logger);
Defensive patterns
Strategy: validation
Validate before calling
function allScenarioTablesPreSeeded(
scenarios: { name: string; seedDataTables?: { name: string }[] }[],
tableIdsByName: Record<string, string>,
): { ok: true } | { ok: false; missing: { scenario: string; table: string }[] } {
const missing: { scenario: string; table: string }[] = [];
for (const s of scenarios)
for (const t of s.seedDataTables ?? [])
if (!tableIdsByName[t.name]) missing.push({ scenario: s.name, table: t.name });
return missing.length ? { ok: false, missing } : { ok: true };
}
const check = allScenarioTablesPreSeeded(allScenarios, tableIdsByName);
if (!check.ok) throw new Error(`pre-seed missing: ${JSON.stringify(check.missing)}`); Type guard
null
Try / catch
try {
await reseedScenarioTables(client, scenario, threadId, tableIdsByName, logger);
} catch (e) {
if (e instanceof Error && /was not pre-seeded before the build/.test(e.message)) {
// re-run pre-seed for the union of all scenario tables, then retry
}
throw e;
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- A case declares ${String(byName.size)} distinct scenario see
- Unknown role: ${msg.role as string}
- Delegated child checkpoint metadata is missing or invalid
- Failed to parse ${label} at ${path}: ${msg}
- Existing manifest at ${manifestPath} is malformed; remove or
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/7e12a308da40d826.
Report an issue: GitHub.