{"record":{"id":"89bbf6f0c453840c","repo":"n8n-io/n8n","slug":"seed-declares-two-agents-with-id-duplicateagent","errorCode":null,"errorMessage":"Seed declares two agents with id \"${duplicateAgentId}\". Each seeded agent is created at its pinned id, so the second would abort the restore — give them distinct ids","messagePattern":"Seed declares two agents with id \"(.+?)\"\\. Each seeded agent is created at its pinned id, so the second would abort the restore — give them distinct ids","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/@n8n/instance-ai/evaluations/harness/conversation-seed.ts","lineNumber":340,"sourceCode":" * Agents get the id pass only: they are addressed by id, and an agent's name\n * appears inside skill prose, where a blanket rename would rewrite instructions\n * the case grades.\n *\n * The workflow name rewrite is applied to `messages` ONLY, never inside\n * `workflows[].nodes` — names are short and human (\"Batch loop\"), so a blanket\n * replace could hit a same-named node and silently alter the restored graph.\n */\nexport function remapSeedArtifactIds(seed: ConversationSeed): ConversationSeed {\n\tif (seed.workflows.length === 0 && seed.agents.length === 0) return seed;\n\n\t// Duplicates collapse in the id Set below, so both entries take ONE fresh id and\n\t// the restore's second `create` on that pinned id aborts the whole seed. Refuse\n\t// here rather than fail mid-restore — `workflows` has no such invariant either,\n\t// but its duplicate NAMES are already refused further down.\n\tconst agentIds = seed.agents.map((agent) => agent.id);\n\tconst duplicateAgentId = agentIds.find((id, index) => agentIds.indexOf(id) !== index);\n\tif (duplicateAgentId !== undefined) {\n\t\tthrow new Error(\n\t\t\t`Seed declares two agents with id \"${duplicateAgentId}\". Each seeded agent is created at ` +\n\t\t\t\t'its pinned id, so the second would abort the restore — give them distinct ids',\n\t\t);\n\t}\n\n\t// Workflows and agents share one id space: a fresh id must miss every original,\n\t// or this sequential replace could rewrite a not-yet-processed artifact's id.\n\tconst originalIds = new Set([\n\t\t...seed.workflows.map((workflow) => workflow.id),\n\t\t...seed.agents.map((agent) => agent.id),\n\t]);\n\tlet serialized = JSON.stringify({\n\t\tmessages: seed.messages,\n\t\tworkflows: seed.workflows,\n\t\tagents: seed.agents,\n\t});\n\t// Longest id first, for the same reason the name pass below sorts: if one id were a\n\t// prefix of another (\"abcdefgh\" / \"abcdefgh12\"), rewriting the short one first would","sourceCodeStart":322,"sourceCodeEnd":358,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/instance-ai/evaluations/harness/conversation-seed.ts#L322-L358","documentation":"Thrown by remapSeedArtifactIds() when two agents in the seed declare the same id. Agents are restored by creating each at its pinned id, so a duplicate id would make the second create collide with the first and abort the whole restore. The harness refuses the fixture up front rather than fail mid-restore. Note: workflow duplicate NAMES are refused separately later (455), but ids are not — agent ids specifically carry this invariant because of the pinned-create semantics.","triggerScenarios":"A seed fixture (inline or scrubbed-from-real) contains two agent entries with the same `id` field. Often from copy-pasting an agent block and forgetting to mint a new id.","commonSituations":"Hand-authored fixture with a duplicated agent block; a scrubbed real trace where two agents happened to share an id after redaction.","solutions":["Give each agent in seed.agents a distinct id (≥8 chars — see 454).","If the fixture was scrubbed from a real thread, re-derive ids so they are unique.","Regenerate the inline seed rather than hand-editing it."],"exampleFix":"// before\nagents: [\n  { id: 'agt-XXXX', name: 'A' },\n  { id: 'agt-XXXX', name: 'B' },\n]\n// after\nagents: [\n  { id: 'agt-XXXXAAAA', name: 'A' },\n  { id: 'agt-YYYYBBBB', name: 'B' },\n]","handlingStrategy":"validation","validationCode":"// Validate agent id uniqueness in the fixture before running.\nfunction assertUniqueAgentIds(seed: ConversationSeed): void {\n  const ids = seed.agents.map((a) => a.id);\n  const dup = ids.find((id, i) => ids.indexOf(id) !== i);\n  if (dup) throw new Error(`Duplicate agent id \"${dup}\" in seed fixture; give each agent a distinct id.`);\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Generate agent ids with the same nano-id generator the runtime uses.","Add a fixture linter that rejects duplicate agent ids at author time.","When scrubbing a real trace, re-mint ids rather than preserving collisions."],"tags":["seed-fixture","agent","validation","id-uniqueness"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}