{"record":{"id":"08ab7f118ba59b09","repo":"affaan-m/ECC","slug":"circular-install-dependency-detected-at-moduleid","errorCode":null,"errorMessage":"Circular install dependency detected at ${moduleId}","messagePattern":"Circular install dependency detected at (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"scripts/lib/install-manifests.js","lineNumber":670,"sourceCode":"        readModuleTargetsOrThrow(module).includes(target)\n        && (!targetAdapter || targetAdapter.supportsModule(module, targetPlanningInput))\n      );\n\n    if (!supportsTarget) {\n      if (dependencyOf) {\n        skippedTargetIds.add(rootRequesterId || dependencyOf);\n        return false;\n      }\n      skippedTargetIds.add(moduleId);\n      return false;\n    }\n\n    if (resolvedIds.has(moduleId)) {\n      return true;\n    }\n\n    if (visitingIds.has(moduleId)) {\n      throw new Error(`Circular install dependency detected at ${moduleId}`);\n    }\n\n    visitingIds.add(moduleId);\n    for (const dependencyId of module.dependencies) {\n      const dependencyResolved = resolveModule(\n        dependencyId,\n        moduleId,\n        rootRequesterId || moduleId\n      );\n      if (!dependencyResolved) {\n        visitingIds.delete(moduleId);\n        if (!dependencyOf) {\n          skippedTargetIds.add(moduleId);\n        }\n        return false;\n      }\n    }\n    visitingIds.delete(moduleId);","sourceCodeStart":652,"sourceCodeEnd":688,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/lib/install-manifests.js#L652-L688","documentation":"resolveInstallPlan detects cycles while depth-first resolving module dependencies: it marks modules in `visitingIds` and throws if it re-enters a module already on the current DFS path. This is a manifest data integrity error — a valid module graph must be acyclic.","triggerScenarios":"Calling resolveInstallPlan (via plan or runTests) when manifests/install-modules.json defines a dependency cycle, e.g. module A lists B in `dependencies` while B lists A (or a longer loop returns to A).","commonSituations":"Hand-editing manifests/install-modules.json and adding a two-way dependency; a refactor merged two modules but left cross-references; an automated script rewired dependencies incorrectly.","solutions":["Open manifests/install-modules.json and trace the dependency cycle starting at moduleId","Break the cycle by removing one of the `dependencies` entries on the loop","Run the install plan tests (`npm test`) to confirm the graph resolves after the edit"],"exampleFix":"// before (manifests/install-modules.json)\n{ \"id\": \"skills-a\", \"dependencies\": [\"skills-b\"] }\n{ \"id\": \"skills-b\", \"dependencies\": [\"skills-a\"] }\n// after\n{ \"id\": \"skills-a\", \"dependencies\": [\"skills-b\"] }\n{ \"id\": \"skills-b\", \"dependencies\": [] }","handlingStrategy":"validation","validationCode":"// Detect cycles in the module graph before resolving\nfunction hasCycle(modulesById, startId) {\n  const seen = new Set();\n  function visit(id, stack) {\n    if (stack.has(id)) return true;\n    if (seen.has(id)) return false;\n    stack.add(id);\n    for (const d of modulesById.get(id)?.dependencies || []) if (visit(d, stack)) return true;\n    stack.delete(id); seen.add(id);\n    return false;\n  }\n  return visit(startId, new Set());\n}","typeGuard":null,"tryCatchPattern":"try {\n  const plan = resolveInstallPlan(options);\n} catch (e) {\n  if (/Circular install dependency/.test(e.message)) {\n    // fix manifests/install-modules.json to remove the cycle\n  } else throw e;\n}","preventionTips":["Keep dependency direction one-way: lower-level modules must never depend on higher-level ones","Add a CI test that asserts the module graph is acyclic","After any manual edit to dependencies, run the manifest tests immediately"],"tags":["install","dependencies","circular-dependency","manifest"],"backgroundTag":"invalid-state-transition","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}