medusajs/medusa · error · Error
Unable to soft delete the ${relation.name}. Circular depende
Error message
Unable to soft delete the ${relation.name}. Circular dependency detected: ${circularDependencyStr} What it means
detectCircularDependency walks the entity relation graph before recursively soft-deleting related records and throws this plain Error when it finds a cycle (A -> B -> ... -> A) that cannot be safely ordered for deletion. It is raised from mikro-orm/utils.ts during mikroOrmUpdateDeletedAtRecursively, which is used by repository soft-delete cascades.
Source
Thrown at packages/core/utils/src/dal/mikro-orm/utils.ts:41
)
for (const relation of relationsToCascade) {
const branchVisited = new Set(Array.from(visited))
const relationEntity =
typeof relation.entity === "function"
? relation.entity()
: relation.entity
const isSelfCircularDependency = isString(relationEntity)
? entityMetadata.className === relationEntity
: entityMetadata.class === relationEntity
if (!isSelfCircularDependency && branchVisited.has(relation.name)) {
const dependencies = Array.from(visited)
dependencies.push(entityMetadata.className)
const circularDependencyStr = dependencies.join(" -> ")
throw new Error(
`Unable to soft delete the ${relation.name}. Circular dependency detected: ${circularDependencyStr}`
)
}
branchVisited.add(relation.name)
const relationEntityMetadata = manager
.getDriver()
.getMetadata()
.get(relation.type)
detectCircularDependency(
manager,
relationEntityMetadata,
branchVisited,
isSelfCircularDependency
)
}
}View on GitHub (pinned to 5e06e544a2)
Solutions
- Inspect the chain printed in the message (A -> B -> A) and remove one direction from the soft-deletable/cascade configuration so the graph becomes acyclic.
- Soft-delete each side explicitly in separate ordered service calls instead of relying on recursive cascade.
- If the inverse relation is only needed for querying, keep it on the model but exclude it from the soft-delete cascade configuration.
- After changing the model, regenerate the module migration if the change affects the schema.
Example fix
// before (module model link config — both directions soft-deletable)
softDelete: () => true // on A.b AND on B.a => cycle
// after
// A.b keeps cascade:
// b: one({ entity: () => B, ... })
// B.a becomes a plain relation without soft-delete cascade (or is soft-deleted manually after A) Defensive patterns
Strategy: fallback
Try / catch
try {
await service.softDelete(ids)
} catch (err) {
if (err instanceof Error && /Circular dependency detected/.test(err.message)) {
// fallback: soft-delete each side explicitly in a safe order
for (const id of ids) await service.softDelete(id, { relations: acyclicSubset })
} else throw err
} Prevention
- Keep soft-delete cascade configuration acyclic when designing reciprocal relations.
- Audit module link/softDelete flags after adding new relations between existing models.
- Prefer explicit ordered soft-deletes in workflows over relying on deep recursive cascade.
When it happens
Trigger: Calling softDelete on an entity whose softDeletable relation chain forms a cycle — e.g. entity A has a soft-deletable many-to-one to B while B also has a soft-deletable relation back to A; custom modules that add reciprocal soft-deletable relations between two models.
Common situations: Adding a new relation to a module data model and marking it soft-deletable without checking the inverse side already cascades back; customization that links e.g. order <-> fulfillment-ish tables in both directions with cascade soft deletes; version upgrades where new relations made an existing pair cyclic.
Related errors
- Nonexistent relations were passed during upsert: ${nonexiste
- Cannot set value ${value} for ${columnName}.
- 42703
- Many-to-one relation ${relation.name} must be set with an ID
AI-assisted analysis of medusajs/medusa@5e06e544a2 (2026-08-27).
Data as JSON: /api/errors/5b1da623aa5a2177.
Report an issue: GitHub.