{"record":{"id":"b370e73ecbd673cd","repo":"angular/angular-cli","slug":"circular-builder-alias-references-detected","errorCode":null,"errorMessage":"Circular builder alias references detected: ","messagePattern":"Circular builder alias references detected: ","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/angular_devkit/architect/node/node-modules-architect-host.ts","lineNumber":141,"sourceCode":"\n  async getBuilderNameForTarget(target: Target): Promise<string> {\n    return this.workspaceHost.getBuilderName(target.project, target.target);\n  }\n\n  /**\n   * Resolve a builder. This needs to be a string which will be used in a dynamic `import()`\n   * clause. This should throw if no builder can be found. The dynamic import will throw if\n   * it is unsupported.\n   * @param builderStr The name of the builder to be used.\n   * @returns All the info needed for the builder itself.\n   */\n  resolveBuilder(\n    builderStr: string,\n    basePath: string = this._root,\n    seenBuilders?: Set<string>,\n  ): Promise<NodeModulesBuilderInfo> {\n    if (seenBuilders?.has(builderStr)) {\n      throw new Error(\n        'Circular builder alias references detected: ' + [...seenBuilders, builderStr],\n      );\n    }\n\n    const [packageName, builderName] = builderStr.split(':', 2);\n    if (!builderName) {\n      throw new Error('No builder name specified.');\n    }\n\n    // Resolve and load the builders manifest from the package's `builders` field, if present\n    const packageJsonPath = localRequire.resolve(packageName + '/package.json', {\n      paths: [basePath],\n    });\n\n    const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8')) as { builders?: string };\n    const buildersManifestRawPath = packageJson['builders'];\n    if (!buildersManifestRawPath) {\n      throw new Error(`Package ${JSON.stringify(packageName)} has no builders defined.`);","sourceCodeStart":123,"sourceCodeEnd":159,"githubUrl":"https://github.com/angular/angular-cli/blob/bb72145f9ab45aee29f523236b3a25cd0813a841/packages/angular_devkit/architect/node/node-modules-architect-host.ts#L123-L159","documentation":"resolveBuilder follows builder alias entries (a builder manifest entry that is itself a 'package:builder' string). It tracks seen builders in a Set and throws if the same builder string is encountered again, preventing infinite recursion from a circular alias chain.","triggerScenarios":"A builders.json manifest entry points (directly or transitively) via string aliases back to itself, e.g. pkg:a -> pkg:b -> pkg:a, and resolveBuilder is called with one of those builders.","commonSituations":"Hand-written or copied builder manifests with mistaken alias entries; refactoring a package and leaving an alias pointing at its own target.","solutions":["Inspect the package's builders.json and break the alias cycle by pointing entries at real implementation objects.","Check all manifest entries in the chain printed in the error message.","Remove the self-referencing alias and define an 'implementation' and 'schema' path directly."],"exampleFix":"// before (builders.json)\n{\"builders\": {\"a\": \"my-pkg:a\", \"b\": \"my-pkg:b\"}}\n// after\n{\"builders\": {\"a\": {\"implementation\": \"./a.impl\", \"schema\": \"./a.schema.json\"}}}","handlingStrategy":"validation","validationCode":"function detectAliasCycle(builders) {\n  const seen = new Set();\n  for (const name of Object.keys(builders)) {\n    let cur = builders[name];\n    seen.clear();\n    while (typeof cur === 'string') {\n      if (seen.has(cur)) throw new Error(`Circular builder alias: ${[...seen, cur].join(' -> ')}`);\n      seen.add(cur);\n      const [pkg, b] = cur.split(':', 2);\n      cur = require(`${pkg}/builders.json`)?.builders?.[b];\n    }\n  }\n}","typeGuard":"const isRealBuilder = (b) => typeof b === 'object' && b !== null && typeof b.implementation === 'string';","tryCatchPattern":"try { await host.resolveBuilder(builderStr); } catch (e) { if (String(e.message).includes('Circular builder alias')) { /* fix the builders.json in the offending package */ } else throw e; }","preventionTips":["Never alias a builder to itself; aliases must terminate at object entries.","Lint builders.json manifests in CI to catch alias cycles.","Keep builder manifests minimal and reviewed."],"tags":["angular","builder","configuration"],"backgroundTag":"circular-reference-detected","analyzedSha":"bb72145f9ab45aee29f523236b3a25cd0813a841","analyzedAt":"2026-08-30T02:47:34.745Z","schemaVersion":2},"datasetVersion":"2026-08-30T03:17:51.788Z"}