{"record":{"id":"9ded6b99bef24f28","repo":"n8n-io/n8n","slug":"nested-sets-do-not-support-multiple-root-entities","errorCode":null,"errorMessage":"Nested sets do not support multiple root entities.","messagePattern":"Nested sets do not support multiple root entities\\.","errorType":"exception","errorClass":"NestedSetMultipleRootError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/typeorm/src/persistence/tree/NestedSetSubjectExecutor.ts","lineNumber":79,"sourceCode":"\n\t\tif (parentNsRight !== undefined) {\n\t\t\tawait this.queryRunner.query(\n\t\t\t\t`UPDATE ${tableName} SET ` +\n\t\t\t\t\t`${leftColumnName} = CASE WHEN ${leftColumnName} > ${parentNsRight} THEN ${leftColumnName} + 2 ELSE ${leftColumnName} END,` +\n\t\t\t\t\t`${rightColumnName} = ${rightColumnName} + 2 ` +\n\t\t\t\t\t`WHERE ${rightColumnName} >= ${parentNsRight}`,\n\t\t\t);\n\n\t\t\tOrmUtils.mergeDeep(\n\t\t\t\tsubject.insertedValueSet,\n\t\t\t\tsubject.metadata.nestedSetLeftColumn!.createValueMap(parentNsRight),\n\t\t\t\tsubject.metadata.nestedSetRightColumn!.createValueMap(parentNsRight + 1),\n\t\t\t);\n\t\t} else {\n\t\t\tconst isUniqueRoot = await this.isUniqueRootEntity(subject, parent);\n\n\t\t\t// Validate if a root entity already exits and throw an exception\n\t\t\tif (!isUniqueRoot) throw new NestedSetMultipleRootError();\n\n\t\t\tOrmUtils.mergeDeep(\n\t\t\t\tsubject.insertedValueSet,\n\t\t\t\tsubject.metadata.nestedSetLeftColumn!.createValueMap(1),\n\t\t\t\tsubject.metadata.nestedSetRightColumn!.createValueMap(2),\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Executes operations when subject is being updated.\n\t */\n\tasync update(subject: Subject): Promise<void> {\n\t\tlet parent = subject.metadata.treeParentRelation!.getEntityValue(subject.entity!); // if entity was attached via parent\n\t\tif (!parent && subject.parentSubject && subject.parentSubject.entity)\n\t\t\t// if entity was attached via children\n\t\t\tparent = subject.parentSubject.entity;\n","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/typeorm/src/persistence/tree/NestedSetSubjectExecutor.ts#L61-L97","documentation":"Nested-set trees model hierarchy with left/right bounds and, by design, support exactly ONE root entity per tree. When inserting a node whose parent is null/undefined (i.e. a candidate root), `NestedSetSubjectExecutor` calls `isUniqueRootEntity`; if a root already exists in the table, it throws `NestedSetMultipleRootError`.","triggerScenarios":"Inserting a second root into a nested-set tree: saving a node with no parent after a root already exists. Bulk-inserting multiple parent-less nodes.","commonSituations":"Choosing the nested-set tree type for data that needs multiple roots (e.g. multiple independent category trees). Seeding the DB and inserting two top-level categories. Migrating from another tree type without consolidating under a single root.","solutions":["Model a single synthetic root and make all other top-level nodes children of it.","Switch the tree strategy to closure-table or materialized-path (`tree.type: 'closure-table'` / `'materialized-path'`), which support multiple roots.","Query for an existing root before inserting; if present, attach the new node as its child instead."],"exampleFix":"// before — second root in a nested-set tree\nconst root2 = new Category(); root2.name = 'root2';\nawait treeRepo.save(root2); // a root already exists → NestedSetMultipleRootError\n\n// after — attach under the existing root\nconst existingRoot = await treeRepo.findRoots();\nroot2.parent = existingRoot[0];\nawait treeRepo.save(root2);","handlingStrategy":"validation","validationCode":"// Before saving a potential nested-set root, check no root exists\nif (!node.parent) {\n  const roots = await treeRepo.findRoots();\n  if (roots.length > 0) {\n    throw new Error('Nested-set tree already has a root; attach this node under it or switch tree type.');\n  }\n}","typeGuard":null,"tryCatchPattern":"try { await treeRepo.save(node); } catch (e) { if (e instanceof NestedSetMultipleRootError) { /* attach under existing root or switch tree type */ } throw e; }","preventionTips":["Pick nested-set only when you need exactly one root; otherwise use closure-table or materialized-path.","Create a single synthetic root at seed time and forbid parent-less inserts thereafter.","Guard insert paths with a roots-exist check before saving a parent-less node."],"tags":["typeorm","tree","nested-set","persistence","constraint"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}