{"record":{"id":"d30f5d46e747c8d7","repo":"n8n-io/n8n","slug":"relation-entitymetadata-name-relation-propert","errorCode":null,"errorMessage":"Relation ${entityMetadata.name}#${relation.propertyName} and ${relation.inverseRelation!.entityMetadata.name}#${relation.inverseRelation!.propertyName} both has cascade remove set. This may lead to unexpected circular removals. Please set cascade remove only from one side of relationship.","messagePattern":"Relation (.+?)#(.+?) and (.+?)#(.+?) both has cascade remove set\\. This may lead to unexpected circular removals\\. Please set cascade remove only from one side of relationship\\.","errorType":"exception","errorClass":"TypeORMError","httpStatus":null,"severity":"error","filePath":"packages/@n8n/typeorm/src/metadata-builder/EntityMetadataValidator.ts","lineNumber":235,"sourceCode":"\t\t\t// todo: if there is a relation, and inverse side is specified only on one side, shall we give error\n\t\t\t// todo: with message like: \"Inverse side is specified only on one side of the relationship. Specify on other side too to prevent confusion\".\n\t\t\t// todo: add validation if there two entities with the same target, and show error message with description of the problem (maybe file was renamed/moved but left in output directory)\n\t\t\t// todo: check if there are multiple columns on the same column applied.\n\t\t\t// todo: check column type if is missing in relational databases (throw new TypeORMError(`Column type of ${type} cannot be determined.`);)\n\t\t\t// todo: include driver-specific checks. for example in mongodb empty prefixes are not allowed\n\t\t\t// todo: if multiple columns with same name - throw exception, including cases when columns are in embeds with same prefixes or without prefix at all\n\t\t\t// todo: if multiple primary key used, at least one of them must be unique or @Index decorator must be set on entity\n\t\t\t// todo: check if entity with duplicate names, some decorators exist\n\t\t});\n\n\t\t// make sure cascade remove is not set for both sides of relationships (can be set in OneToOne decorators)\n\t\tentityMetadata.relations.forEach((relation) => {\n\t\t\tconst isCircularCascadeRemove =\n\t\t\t\trelation.isCascadeRemove &&\n\t\t\t\trelation.inverseRelation &&\n\t\t\t\trelation.inverseRelation!.isCascadeRemove;\n\t\t\tif (isCircularCascadeRemove)\n\t\t\t\tthrow new TypeORMError(\n\t\t\t\t\t`Relation ${entityMetadata.name}#${\n\t\t\t\t\t\trelation.propertyName\n\t\t\t\t\t} and ${relation.inverseRelation!.entityMetadata.name}#${\n\t\t\t\t\t\trelation.inverseRelation!.propertyName\n\t\t\t\t\t} both has cascade remove set. ` +\n\t\t\t\t\t\t`This may lead to unexpected circular removals. Please set cascade remove only from one side of relationship.`,\n\t\t\t\t);\n\t\t}); // todo: maybe better just deny removal from one to one relation without join column?\n\n\t\tentityMetadata.eagerRelations.forEach((relation) => {});\n\t}\n\n\t/**\n\t * Validates dependencies of the entity metadatas.\n\t */\n\tprotected validateDependencies(entityMetadatas: EntityMetadata[]) {\n\t\tconst graph = new DepGraph();\n\t\tentityMetadatas.forEach((entityMetadata) => {","sourceCodeStart":217,"sourceCodeEnd":253,"githubUrl":"https://github.com/n8n-io/n8n/blob/5ac6606e81f67bb9534255570cd4e86fd8101eee/packages/@n8n/typeorm/src/metadata-builder/EntityMetadataValidator.ts#L217-L253","documentation":"TypeORM forbids `cascade: ['remove']` (or `onDelete: 'CASCADE'` via cascade) being enabled on BOTH sides of a relation, because removing one entity would cascade-remove its partner, which would cascade-remove the first again, producing unbounded circular deletion. The validator walks every relation and rejects the pairing when `isCascadeRemove` is true on both sides.","triggerScenarios":"A bidirectional @OneToOne where both sides declare `cascade: ['remove']` (or one side uses `cascade: ['remove']` and the inverse uses `onDelete='CASCADE'` that TypeORM maps to isCascadeRemove). Two @ManyToOne/@OneToMany entities each marking remove cascade.","commonSituations":"Copy-pasting cascade config from one side to the inverse side. Adding `cascade: true` (which includes remove) on both ends of a 1:1 user<->profile link.","solutions":["Remove `cascade: ['remove']` (or `cascade: true`) from exactly one side of the relation; keep it only on the owning/parent side.","If both ends genuinely must clean up, drive removal from application code (explicit `repository.remove`) instead of DB cascade.","Prefer `onDelete: 'SET NULL'` on the FK side instead of cascade remove to avoid the cycle."],"exampleFix":"// before — both sides cascade\n@Entity() class Profile { @OneToOne(() => User, u => u.profile, { cascade: ['remove'] }) user: User; }\n@Entity() class User { @OneToOne(() => Profile, p => p.user, { cascade: ['remove'] }) profile: Profile; }\n\n// after — only the owning side cascades\n@Entity() class Profile { @OneToOne(() => User, u => u.profile) user: User; }\n@Entity() class User { @OneToOne(() => Profile, p => p.user, { cascade: ['remove'] }) profile: Profile; }","handlingStrategy":"validation","validationCode":"// After building metadata, assert no relation has both sides cascading remove\nfor (const meta of dataSource.entityMetadatas) {\n  for (const r of meta.relations) {\n    if (r.isCascadeRemove && r.inverseRelation?.isCascadeRemove) {\n      throw new Error(`Double cascade-remove on ${meta.name}#${r.propertyName} <-> ${r.inverseRelation.entityMetadata.name}#${r.inverseRelation.propertyName}`);\n    }\n  }\n}","typeGuard":null,"tryCatchPattern":"try { await dataSource.initialize(); } catch (e) { if (e instanceof TypeORMError && /both has cascade remove set/) { /* locate the OneToOne pair named in the message */ } throw e; }","preventionTips":["Put `cascade` on exactly one side of a bidirectional relation and document which side owns it.","Avoid `cascade: true` (which silently includes remove); list the operations you mean.","Review inverse-side decorators whenever you add cascade to the owning side."],"tags":["typeorm","relations","cascade","schema-validation","one-to-one"],"backgroundTag":null,"analyzedSha":"5ac6606e81f67bb9534255570cd4e86fd8101eee","analyzedAt":"2026-08-12T05:26:35.080Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}