{"record":{"id":"26441da0543be027","repo":"elsa-workflows/elsa-core","slug":"cyclic-dependency-found","errorCode":null,"errorMessage":"Cyclic dependency found","messagePattern":"Cyclic dependency found","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/common/Elsa.Features/Extensions/EnumerableTopologicalSortExtensions.cs","lineNumber":36,"sourceCode":"        foreach (var item in source)\n            Visit(item, visited, sorted, dependencies, throwOnCycle);\n\n        return sorted;\n    }\n\n    private static void Visit<T>(T item, ISet<T> visited, ICollection<T> sorted, Func<T, IEnumerable<T>> dependencies, bool throwOnCycle)\n    {\n        if (visited.Add(item))\n        {\n            foreach (var dep in dependencies(item))\n                Visit(dep, visited, sorted, dependencies, throwOnCycle);\n\n            sorted.Add(item);\n        }\n        else\n        {\n            if (throwOnCycle && !sorted.Contains(item))\n                throw new Exception(\"Cyclic dependency found\");\n        }\n    }\n}","sourceCodeStart":18,"sourceCodeEnd":39,"githubUrl":"https://github.com/elsa-workflows/elsa-core/blob/fe9217bdfa0e27f0e09e45006eb6898f616e513d/src/common/Elsa.Features/Extensions/EnumerableTopologicalSortExtensions.cs#L18-L39","documentation":"EnumerableTopologicalSortExtensions performs a depth-first topological sort of dependency-ordered items (used by Elsa features). When a cycle is detected and throwOnCycle is enabled, it throws a generic Exception('Cyclic dependency found').","triggerScenarios":"Registering features (or any items with Dependencies) such that A depends on B and B (directly or transitively) depends on A, then running the topological sort.","commonSituations":"Custom Elsa feature declaring a dependency on a feature that (transitively) depends back on it; copying dependency lists between features creating a loop; plugin conflicts after package upgrades.","solutions":["Break the cycle by removing the back-dependency from one feature's Dependencies list","Move shared logic into a lower-level feature that both depend on","Review the feature dependency graph and ensure it forms a DAG"],"exampleFix":"// before\nclass FeatureA : FeatureBase { public override IEnumerable<Type> Dependencies => new[] { typeof(FeatureB) }; }\nclass FeatureB : FeatureBase { public override IEnumerable<Type> Dependencies => new[] { typeof(FeatureA) }; }\n// after\nclass FeatureB : FeatureBase { public override IEnumerable<Type> Dependencies => Array.Empty<Type>(); }","handlingStrategy":"validation","validationCode":"// detect cycle before registering\nvar deps = new Dictionary<Type, IEnumerable<Type>> { [typeof(FeatureA)] = typeof(FeatureA).GetDependencies() /* etc */ };\n// run sort with throwOnCycle: false and inspect remaining unsorted items","typeGuard":null,"tryCatchPattern":"try { features.TSort(f => f.Dependencies, f => f.DependsOn, throwOnCycle: true); } catch (Exception e) when (e.Message == \"Cyclic dependency found\") { logger.LogError(\"Feature dependency cycle detected\"); }","preventionTips":["Keep feature dependency lists one-directional","Extract shared services into a base feature both features depend on","Review dependency graph after adding new features or upgrades"],"tags":["dotnet","topological-sort","dependency-cycle","features"],"backgroundTag":"internal-invariant-violation","analyzedSha":"fe9217bdfa0e27f0e09e45006eb6898f616e513d","analyzedAt":"2026-09-13T20:32:34.702Z","contentChangedAt":"2026-09-13T20:32:34.702Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}