{"record":{"id":"9b325db88a1c0f5a","repo":"angular/angular-cli","slug":"circular-dependency-found","errorCode":null,"errorMessage":"Circular dependency found.","messagePattern":"Circular dependency found\\.","errorType":"exception","errorClass":"CircularDependencyFoundException","httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/core/src/utils/partially-ordered-set.ts","lineNumber":30,"sourceCode":"  constructor() {\n    super('One of the dependencies is not part of the set.');\n  }\n}\nexport class CircularDependencyFoundException extends BaseException {\n  constructor() {\n    super('Circular dependencies found.');\n  }\n}\n\n/**\n * @deprecated Use standard arrays and ensure correct insertion order instead.\n */\nexport class PartiallyOrderedSet<T> {\n  private _items = new Map<T, Set<T>>();\n\n  protected _checkCircularDependencies(item: T, deps: Set<T>): void {\n    if (deps.has(item)) {\n      throw new CircularDependencyFoundException();\n    }\n\n    deps.forEach((dep) => this._checkCircularDependencies(item, this._items.get(dep) || new Set()));\n  }\n\n  clear(): void {\n    this._items.clear();\n  }\n  has(item: T): boolean {\n    return this._items.has(item);\n  }\n  get size(): number {\n    return this._items.size;\n  }\n  forEach(\n    callbackfn: (value: T, value2: T, set: PartiallyOrderedSet<T>) => void,\n    thisArg?: any, // eslint-disable-line @typescript-eslint/no-explicit-any\n  ): void {","sourceCodeStart":12,"sourceCodeEnd":48,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/core/src/utils/partially-ordered-set.ts#L12-L48","documentation":"PartiallyOrderedSet._checkCircularDependencies walks the dependency graph transitively from a candidate item; if it ever reaches the item itself, the graph has a cycle and it throws CircularDependencyFoundException ('Circular dependency found.'). A topologically ordered structure cannot represent cycles, so insertion is what rejects them. It is invoked from add() and recursively from itself.","triggerScenarios":"add('a', ['b']) where 'b' (directly or transitively via _items dependency sets) already depends on 'a'; e.g. add('a', []), add('b', ['a']), then add('a', ['b']).","commonSituations":"Registering schema formats or tasks (JobRegistry/schema utilities use partially ordered sets) where mutual registrations create a cycle, dynamically generated registration order in plugin systems, typo making two items depend on each other.","solutions":["Break the cycle: remove the dependency edge that closes the loop from your registration calls","Register items in dependency order without back-references; model mutual needs with a shared third item","If the cycle is legitimate, use a plain Map/array instead of PartiallyOrderedSet since it cannot represent cyclic graphs"],"exampleFix":"// before\npos.add('a', []);\npos.add('b', ['a']);\npos.add('a', ['b']); // circular\n// after\npos.add('a', []);\npos.add('b', ['a']); // keep 'a' dependency-free of 'b'","handlingStrategy":"try-catch","validationCode":"function hasCycle(items: Map<string, Set<string>>): boolean {\n  const visiting = new Set<string>(), done = new Set<string>();\n  const visit = (n: string): boolean => {\n    if (visiting.has(n)) return true;\n    if (done.has(n)) return false;\n    visiting.add(n);\n    for (const d of items.get(n) || []) if (visit(d)) return true;\n    visiting.delete(n); done.add(n);\n    return false;\n  };\n  for (const n of items.keys()) if (visit(n)) return true;\n  return false;\n}\n// call before mutating registration order","typeGuard":"null","tryCatchPattern":"try {\n  pos.add(item, deps);\n} catch (e) {\n  if ((e as Error).message === 'Circular dependency found.') {\n    // log item + deps and registration history to locate the closing edge\n  }\n  throw e;\n}","preventionTips":["Keep a registration log (item -> deps) so cycles are easy to reconstruct on failure","Enforce layered registration: lower-level items first, no upward dependencies","Add a cycle-detection unit test around your registration sequence","Avoid dynamic re-registration that can close back-references"],"tags":["circular-dependency","topological-sort","data-structure"],"backgroundTag":"circular-dependency","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T08:17:16.595Z"}