angular/angular-cli · error · CircularCollectionException
Circular collection reference "${name}".
Error message
Circular collection reference "${name}". What it means
The schematics engine builds a collection's inheritance chain by recursively resolving every collection listed in its "extends" field. When a collection ultimately extends itself (directly or through a cycle like A extends B, B extends A), the recursion would never terminate, so _createCollectionDescription tracks visited collection names in a set and throws CircularCollectionException when it revisits one. This is a configuration error in the collection definition files, not a runtime condition.
Source
Thrown at packages/angular_devkit/schematics/src/engine/engine.ts:221
collection = new CollectionImpl<CollectionT, SchematicT>(description, this, bases);
this._collectionCache.set(name, collection);
this._schematicCache.set(collection, new Map());
return collection;
}
private _createCollectionDescription(
name: string,
requester?: CollectionDescription<CollectionT>,
parentNames?: Set<string>,
): [CollectionDescription<CollectionT>, Array<CollectionDescription<CollectionT>>] {
const description = this._host.createCollectionDescription(name, requester);
if (!description) {
throw new UnknownCollectionException(name);
}
if (parentNames && parentNames.has(description.name)) {
throw new CircularCollectionException(name);
}
const bases = new Array<CollectionDescription<CollectionT>>();
if (description.extends) {
parentNames = (parentNames || new Set<string>()).add(description.name);
for (const baseName of description.extends) {
const [base, baseBases] = this._createCollectionDescription(
baseName,
description,
new Set(parentNames),
);
bases.unshift(base, ...baseBases);
}
}
return [description, bases];
}View on GitHub (pinned to bb72145f9a)
Solutions
- Open the collection.json for the named collection and inspect its "extends" array for a self-reference or mutual reference with another collection.
- Break the cycle: remove the offending entry from "extends" (the base collection should not inherit from the derived one).
- If inheritance was meant to reuse schematics, inline or reorganize the shared schematics instead of creating a circular extends chain.
- Pin/upgrade to fixed versions of third-party collections whose extends chains were erroneous.
Example fix
// before — collection.json of "my-utils"
{
"name": "my-utils",
"extends": ["my-base"]
}
// and collection.json of "my-base"
{
"name": "my-base",
"extends": ["my-utils"]
}
// after — remove the cycle; only the derived collection extends
// my-utils/collection.json
{
"name": "my-utils",
"extends": ["my-base"]
}
// my-base/collection.json
{
"name": "my-base"
} Defensive patterns
Strategy: validation
Validate before calling
import * as fs from 'fs';
function hasCircularExtends(collectionJsonPaths: string[]): boolean {
const graph = new Map<string, string[]>();
for (const p of collectionJsonPaths) {
const json = JSON.parse(fs.readFileSync(p, 'utf8'));
graph.set(json.name, json.extends ?? []);
}
const visiting = new Set<string>(), visited = new Set<string>();
const dfs = (n: string): boolean => {
if (visiting.has(n)) return true;
if (visited.has(n)) return false;
visiting.add(n);
for (const b of graph.get(n) ?? []) if (dfs(b)) return true;
visiting.delete(n); visited.add(n);
return false;
};
return [...graph.keys()].some(dfs);
} Try / catch
import { CircularCollectionException } from '@angular-devkit/schematics';
try {
const collection = engine.createCollection('my-collection');
} catch (e) {
if (e instanceof CircularCollectionException) {
// fix collection.json "extends" chain
} else { throw e; }
} Prevention
- Keep "extends" chains one-directional: a base collection must never extend a derived one.
- Add a unit test that calls createCollection on every collection you ship to catch cycles at CI time.
- Lint collection.json files with a small script validating that extends references resolve and are acyclic.
When it happens
Trigger: Calling engine.createCollection(name) (which delegates to _createCollectionDescription) where the collection.json of "name" contains an "extends" array that includes a collection whose own "extends" chain leads back to a collection already in the parentNames set — e.g. collection A extends B and B extends A, or A extends A.
Common situations: Hand-editing or scaffolding a custom collection.json and adding a wrong entry to "extends"; refactoring/copying collections and accidentally pointing two collections at each other; publishing a tooling collection with an extends typo that only surfaces when a user runs a schematic from it.
Related errors
- Schematic "${name}" not found in collection "${collection.na
- Option "project" is required.
- Project is not defined in this workspace.
- Targets are not defined for this project.
- Unregistered task "${name}"${addendum}.
AI-assisted analysis of angular/angular-cli@bb72145f9a (2026-08-30).
Data as JSON: /api/errors/a5e48080fe5a5092.
Report an issue: GitHub.