{"record":{"id":"4673eb6ec12a9739","repo":"angular/angular-cli","slug":"circular-schematic-reference-detected-json-stri","errorCode":null,"errorMessage":"Circular schematic reference detected: ${JSON.stringify(Array.from(references))}","messagePattern":"Circular schematic reference detected: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/schematics/tools/node-module-engine-host.ts","lineNumber":41,"sourceCode":"  constructor(name: string) {\n    super(`Package ${JSON.stringify(name)} was found but does not support schematics.`);\n  }\n}\n\n/**\n * A simple EngineHost that uses NodeModules to resolve collections.\n */\nexport class NodeModulesEngineHost extends FileSystemEngineHostBase {\n  constructor(private readonly paths?: string[]) {\n    super();\n  }\n\n  private resolve(name: string, requester?: string, references = new Set<string>()): string {\n    // Keep track of the package requesting the schematic, in order to avoid infinite recursion\n    if (requester) {\n      if (references.has(requester)) {\n        references.add(requester);\n        throw new Error(\n          'Circular schematic reference detected: ' + JSON.stringify(Array.from(references)),\n        );\n      } else {\n        references.add(requester);\n      }\n    }\n\n    let collectionPath: string | undefined = undefined;\n    const resolveOptions = {\n      paths: requester ? [dirname(requester), ...(this.paths || [])] : this.paths,\n    };\n\n    // Try to resolve as a package\n    try {\n      const packageJsonPath = require.resolve(`${name}/package.json`, resolveOptions);\n      const { schematics } = require(packageJsonPath);\n\n      if (!schematics || typeof schematics !== 'string') {","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/schematics/tools/node-module-engine-host.ts#L23-L59","documentation":"NodeModuleEngineHost.resolve tracks the set of packages involved while resolving a schematic collection through node module resolution. If a package reappears as the requester (i.e. package A requires B which requires A, directly or transitively), resolve throws this Error with the full cycle of package names, preventing infinite recursion.","triggerScenarios":"Resolving a collection whose package.json (or an intermediate dependency's) points back to a package already in the `references` set — e.g. package A's schematic extends/references collection of B, and B references A; `references.has(requester)` becomes true.","commonSituations":"Two schematic packages that `extends` each other's collections; a package declaring itself as its own dependency or extending its own collection; accidentally publishing a collection.json whose `extends` points at the same package name; circular npm dependencies introduced by a refactor.","solutions":["Inspect the package list in the error to identify the cycle (e.g. [\"@a/pkg\", \"@b/pkg\"]).","Break the cycle: make one package's collection not `extends` or reference the other (inline the shared schematics or extract a third base package).","Check the offending package.json/collection.json `extends`/dependency fields and remove the self- or circular reference.","Rebuild and reinstall the affected local packages, then retry."],"exampleFix":"// before (pkg-b collection.json)\n{ \"extends\": [\"@a/pkg\"], \"schematics\": { ... } } // while @a/pkg collection.json extends @b/pkg\n// after (break the cycle)\n{ \"schematics\": { ... } } // remove the extends entry from one of the two packages","handlingStrategy":"try-catch","validationCode":"// Detect extends cycles before loading (graph check over collection.json extends fields)\nfunction detectCycle(graph: Record<string, string[]>, start: string) {\n  const seen = new Set<string>();\n  const dfs = (n: string): boolean => {\n    if (n === start && seen.size) return true;\n    if (seen.has(n)) return true;\n    seen.add(n);\n    return (graph[n] || []).some(dfs);\n  };\n  if (dfs(start)) throw new Error(`Circular collection extends involving ${start}`);\n}","typeGuard":null,"tryCatchPattern":"try {\n  const collection = engine.createCollection(name);\n} catch (e) {\n  if (String(e.message).includes('Circular schematic reference detected')) {\n    console.error(e.message); // lists the cycle; break it in the listed package's collection.json\n    process.exitCode = 1;\n  } else throw e;\n}","preventionTips":["Never let two collections `extends` each other; extract shared schematics into a base package.","Never set a package's collection `extends` (or dependency) to itself.","Run a dependency-cycle check (e.g. madge) over collection extends graphs in CI."],"tags":["schematics","module-resolution","circular-dependency"],"backgroundTag":"circular-reference-detected","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}