{"record":{"id":"78f0f24f5d1ceeaa","repo":"puppetlabs/puppet","slug":"module-name-version-requested-invalid-d","errorCode":null,"errorMessage":"'%{module_name}' (%{version}) requested; Invalid dependency cycle","messagePattern":"'%(.+?)' \\(%(.+?)\\) requested; Invalid dependency cycle","errorType":"exception","errorClass":"Puppet::ModuleTool::Errors::InvalidDependencyCycleError","httpStatus":null,"severity":"error","filePath":"lib/puppet/module_tool/shared_behaviors.rb","lineNumber":104,"sourceCode":"        range = begin\n          Puppet::Module.parse_range(@version)\n        rescue\n          Puppet::Module.parse_range('>= 0.0.0')\n        end\n      else\n        range = (@conditions[mod]).map do |r|\n          Puppet::Module.parse_range(r[:dependency])\n        rescue\n          Puppet::Module.parse_range('>= 0.0.0')\n        end.inject(&:&)\n      end\n\n      if @action == :install && seen.include?(mod)\n        next if range === seen[mod][:semver]\n\n        req_module   = @module_name\n        req_versions = @versions[@module_name.to_s].map { |v| v[:semver] }\n        raise InvalidDependencyCycleError,\n              :module_name => mod,\n              :source => (source + [{ :name => mod, :version => source.last[:dependency] }]),\n              :requested_module => req_module,\n              :requested_version => @version || annotated_version(req_module, req_versions),\n              :conditions => @conditions\n      end\n\n      if !(forced? || @installed[mod].empty? || source.last[:name] == :you)\n        next if range === SemanticPuppet::Version.parse(@installed[mod].first.version)\n\n        action = :upgrade\n      elsif @installed[mod].empty?\n        action = :install\n      end\n\n      if action == :upgrade\n        @conditions.each { |_, conds| conds.delete_if { |c| c[:module] == mod } }\n      end","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/module_tool/shared_behaviors.rb#L86-L122","documentation":"Raised by the module tool dependency resolver during install when it revisits a module it has already resolved ('seen') and the version range accumulated so far no longer accepts the release already chosen for it (range === seen[mod][:semver] fails). Despite the name it covers both true cycles (A depends on B depends on A) and chains that circle back with conflicting version conditions. The error carries the module name, the dependency chain and the collected conditions.","triggerScenarios":"Running 'puppet module install' (action == :install) where the dependency graph loops back to an already-selected module with a tighter range: A requires B >= 2.0, B requires A < 2.0, so when the resolver returns to A the previously selected A 2.1.0 no longer satisfies the intersected range.","commonSituations":"metadata.json dependency chains that reference each other (mutual dependencies); two paths through the graph constraining the same module to disjoint ranges; hand-edited metadata.json leaving a module depending on its own dependents; renamed module forks that still cross-reference the old name.","solutions":["Break the cycle: edit the offending metadata.json files so no module transitively depends on itself (usually by removing or renaming the dependency that loops back).","Align version_requirement ranges along the cycle so every revisit accepts the same shared release (e.g. widen '>= 2.1.0' to '>= 2.0.0 < 3.0.0').","Install a different release of the top-level module whose graph resolves cleanly: puppet module install author/mod --version X.Y.Z.","As a last resort for local experiments only, bypass resolution with --force (forced? skips these checks); never in production module directories."],"exampleFix":"# before: mod-a metadata depends on mod-b, and mod-b metadata depends back on mod-a\n{\"name\": \"author/mod-a\", \"dependencies\": [{\"name\": \"author/mod-b\", \"version_requirement\": \">= 1.0.0\"}]}\n\n# after: remove the back-edge in mod-b so the graph is acyclic\n{\"name\": \"author/mod-b\", \"dependencies\": [{\"name\": \"puppetlabs/stdlib\", \"version_requirement\": \">= 8.0.0 < 10.0.0\"}]}","handlingStrategy":"try-catch","validationCode":"require 'semantic_puppet'\n# resolve locally before installing: intersect all ranges imposed on each module\nranges = constraints_on(mod).map { |r| SemanticPuppet::VersionRange.parse(r) }\nshared = ranges.inject(&:&) # intersection collapses to nothing on conflict\nabort 'conflicting constraints on ' + mod unless shared\nabort 'no release satisfies constraints' unless releases.any? { |v| shared === SemanticPuppet::Version.parse(v) }","typeGuard":null,"tryCatchPattern":"begin\n  # puppet module install author/mod\nrescue Puppet::ModuleTool::Errors::InvalidDependencyCycleError => e\n  warn e.message # names the module, version and the cycle conditions\n  exit 1\nend","preventionTips":["Keep each module's dependency graph acyclic; audit with 'puppet module list --tree' after adding dependencies.","When forking or renaming modules, update every metadata.json that referenced the old name to avoid phantom back-edges.","Prefer broad ranges ('>= x.y < z') over exact pins on transitive dependencies.","Validate packages in CI with 'puppet module build' plus a resolve dry-run before publishing."],"tags":["puppet","ruby","module-tool","dependency-resolution","metadata"],"backgroundTag":"dependency-cycle-detected","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}