n8n-io/n8n · error · TypeORMError
Circular eager relations are disallowed. ${entityMetadata.ta
Error message
Circular eager relations are disallowed. ${entityMetadata.targetName}#${relation.propertyPath} contains "eager: true", and its inverse side ${relation.inverseEntityMetadata.targetName}#${relation.inverseRelation.propertyPath} contains "eager: true" as well. Remove "eager: true" from one side of the relation. What it means
TypeORM rejects relations where BOTH sides are marked `eager: true`. Eager loading pulls the related rows on every fetch of the parent; if each side is eager, loading A would load B which would eagerly load A again, recursing without end. The validator scans `entityMetadata.eagerRelations` and throws when an inverse relation is also eager.
Source
Thrown at packages/@n8n/typeorm/src/metadata-builder/EntityMetadataValidator.ts:279
});
});
try {
graph.overallOrder();
} catch (err) {
throw new CircularRelationsError(
err.toString().replace('Error: Dependency Cycle Found: ', ''),
);
}
}
/**
* Validates eager relations to prevent circular dependency in them.
*/
protected validateEagerRelations(entityMetadatas: EntityMetadata[]) {
entityMetadatas.forEach((entityMetadata) => {
entityMetadata.eagerRelations.forEach((relation) => {
if (relation.inverseRelation && relation.inverseRelation.isEager)
throw new TypeORMError(
`Circular eager relations are disallowed. ` +
`${entityMetadata.targetName}#${relation.propertyPath} contains "eager: true", and its inverse side ` +
`${relation.inverseEntityMetadata.targetName}#${relation.inverseRelation.propertyPath} contains "eager: true" as well.` +
` Remove "eager: true" from one side of the relation.`,
);
});
});
}
}
View on GitHub (pinned to 5ac6606e81)
Solutions
- Remove `eager: true` from one side; keep eager only on the side you most often traverse.
- Better: drop `eager` entirely and load relations explicitly via `relations: [...]` in find options or QueryBuilder `leftJoinAndSelect`.
- Use `relation.load()` (lazy relation) on the side you rarely need.
Example fix
// before
@OneToMany(() => Order, o => o.customer, { eager: true }) orders: Order[];
@ManyToOne(() => Customer, c => c.orders, { eager: true }) customer: Customer;
// after — eager on one side only
@OneToMany(() => Order, o => o.customer, { eager: true }) orders: Order[];
@ManyToOne(() => Customer, c => c.orders) customer: Customer; Defensive patterns
Strategy: validation
Validate before calling
for (const m of dataSource.entityMetadatas) {
for (const r of m.eagerRelations) {
if (r.inverseRelation?.isEager) {
throw new Error(`Mutual eager: ${m.name}#${r.propertyName} <-> ${r.inverseRelation.entityMetadata.name}`);
}
}
} Try / catch
try { await dataSource.initialize(); } catch (e) { if (e instanceof TypeORMError && /Circular eager relations/) { /* drop eager:true from one side */ } throw e; } Prevention
- Default to non-eager relations and load explicitly via `relations`/`leftJoinAndSelect`.
- If you use `eager: true`, place it on one side only — the side you traverse most.
- Add a lint/review rule: any PR adding `eager: true` must justify it.
When it happens
Trigger: Two entities with a bidirectional @OneToMany/@ManyToOne or @ManyToMany where both decorators carry `eager: true`. A @OneToOne with `eager: true` on the owner and also on the inverse.
Common situations: Adding `eager: true` to make 'the relation always loads' on both ends for convenience. Copying an eager relation to its inverse during a refactor.
Related errors
- OnUpdateType "${relation.onUpdate}" is not valid for ${drive
- Relation ${entityMetadata.name}#${relation.propertyName} and
- Circular relations detected: ${path}. To resolve this issue
- Referenced column ${joinColumn.referencedColumnName} was not
- Referenced column ${joinColumn.referencedColumnName} was not
AI-assisted analysis of n8n-io/n8n@5ac6606e81 (2026-08-12).
Data as JSON: /api/errors/f9622e0a5ef1e38a.
Report an issue: GitHub.