n8n-io/n8n · error · CannotAttachTreeChildrenEntityError
Cannot attach entity "${entityName}" to its parent. Please m
Error message
Cannot attach entity "${entityName}" to its parent. Please make sure parent is saved in the database before saving children nodes. What it means
For closure-table trees, `ClosureSubjectExecutor` inserts the ancestor/descendant rows linking a child to its parent. For each closure-junction descendant column it reads the parent's value via `referencedColumn.getEntityValue(parent)`; if that value is falsy (no parent id), the parent was never persisted and the link cannot be written, so it throws `CannotAttachTreeChildrenEntityError`.
Source
Thrown at packages/@n8n/typeorm/src/persistence/tree/ClosureSubjectExecutor.ts:85
);
const childEntityIds1 = subject.metadata.primaryColumns.map((column) => {
queryParams.push(
column.getEntityValue(
subject.insertedValueSet ? subject.insertedValueSet : subject.entity!,
),
);
return this.queryRunner.connection.driver.createParameter(
'child_entity_' + column.databaseName,
queryParams.length - 1,
);
});
const whereCondition = subject.metadata.closureJunctionTable.descendantColumns.map(
(column) => {
const columnName = escape(column.databaseName);
const parentId = column.referencedColumn!.getEntityValue(parent);
if (!parentId) throw new CannotAttachTreeChildrenEntityError(subject.metadata.name);
queryParams.push(parentId);
const parameterName = this.queryRunner.connection.driver.createParameter(
'parent_entity_' + column.referencedColumn!.databaseName,
queryParams.length - 1,
);
return `${columnName} = ${parameterName}`;
},
);
await this.queryRunner.query(
`INSERT INTO ${tableName} (${[...ancestorColumnNames, ...descendantColumnNames].join(
', ',
)}) ` +
`SELECT ${ancestorColumnNames.join(', ')}, ${childEntityIds1.join(
', ',
)} FROM ${tableName} WHERE ${whereCondition.join(' AND ')}`,
queryParams,View on GitHub (pinned to 5ac6606e81)
Solutions
- Save the parent first and await it, then assign it to the child and save the child: `await repo.save(parent); child.parent = parent; await repo.save(child);`
- Use `TreeRepository.save` on the root of a fully-built tree so TypeORM persists in root-first order.
- Ensure the parent's primary key column is selected/loaded (not projected away).
Example fix
// before const parent = new Category(); parent.name = 'root'; const child = new Category(); child.name = 'sub'; child.parent = parent; await repo.save(child); // parent has no id yet → error // after await repo.save(parent); child.parent = parent; await repo.save(child);
Defensive patterns
Strategy: validation
Validate before calling
// Before saving a tree child, ensure its parent has an id
const parentPk = dataSource.getMetadata(Parent).primaryColumns.map(c => c.getEntityValue(child.parent));
if (parentPk.some(v => v == null)) {
throw new Error('Parent has no id — save the parent before the child');
} Type guard
function parentHasId<P>(parent: P, pk: keyof P): parent is P & Record<typeof pk, NonNullable<P[typeof pk]>> {
return parent != null && (parent as any)[pk] != null;
} Try / catch
try { await treeRepo.save(child); } catch (e) { if (e instanceof CannotAttachTreeChildrenEntityError) { /* await save(parent) first */ } throw e; } Prevention
- Always save root-first when building a tree in memory.
- Use TreeRepository.save on the root of a fully-built tree.
- Ensure find queries for parents select the primary key column.
When it happens
Trigger: Saving a tree child whose parent is a brand-new entity not yet flushed, so the parent has no id. Calling `treeRepository.save(child)` after assigning `child.parent = new ParentEntity()` without saving the parent first.
Common situations: Building the whole tree in memory then saving leaf-first instead of root-first. Forgetting to await the parent's save before saving children. A parent loaded from a query whose PK column wasn't selected.
Related errors
- Nested sets do not support multiple root entities.
- Tree repositories are not supported in ${driver.options.type
- Cannot ${operation}, given value must be instance of entity
- Removed entity "${subject.metadata.name}" is also scheduled
- Internal error. Subject ${subject.metadata.targetName} must
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/212d6052939f2d25.
Report an issue: GitHub.