{"record":{"id":"5077cf2a1de346b7","repo":"puppetlabs/puppet","slug":"one-or-more-resource-dependency-cycles-detected-in","errorCode":null,"errorMessage":"One or more resource dependency cycles detected in graph","messagePattern":"One or more resource dependency cycles detected in graph","errorType":"exception","errorClass":"Puppet::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/transaction.rb","lineNumber":170,"sourceCode":"        Puppet.log_exception(detail, _(\"post_resource_eval failed for provider %{provider}\") % { provider: provider })\n      end\n\n      persistence.save if persistence.enabled?(catalog)\n    end\n\n    # Graph cycles are returned as an array of arrays\n    # - outer array is an array of cycles\n    # - each inner array is an array of resources involved in a cycle\n    # Short circuit resource evaluation if we detect cycle(s) in the graph. Mark\n    # each corresponding resource as failed in the report before we fail to\n    # ensure accurate reporting.\n    graph_cycle_handler = lambda do |cycles|\n      cycles.flatten.uniq.each do |resource|\n        # We add a failed resource event to the status to ensure accurate\n        # reporting through the event manager.\n        resource_status(resource).fail_with_event(_('resource is part of a dependency cycle'))\n      end\n      raise Puppet::Error, _('One or more resource dependency cycles detected in graph')\n    end\n\n    # Generate the relationship graph, set up our generator to use it\n    # for eval_generate, then kick off our traversal.\n    generator.relationship_graph = relationship_graph\n    progress = 0\n    relationship_graph.traverse(:while => continue_while,\n                                :pre_process => pre_process,\n                                :overly_deferred_resource_handler => overly_deferred_resource_handler,\n                                :canceled_resource_handler => canceled_resource_handler,\n                                :graph_cycle_handler => graph_cycle_handler,\n                                :teardown => teardown) do |resource|\n      progress += 1\n      if resource.is_a?(Puppet::Type::Component)\n        Puppet.warning _(\"Somehow left a component in the relationship graph\")\n      else\n        if Puppet[:evaltrace] && @catalog.host_config?\n          resource.info _(\"Starting to evaluate the resource (%{progress} of %{total})\") % { progress: progress, total: relationship_graph.size }","sourceCodeStart":152,"sourceCodeEnd":188,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/transaction.rb#L152-L188","documentation":"When the transaction traverses the relationship graph, Puppet::Graph::Ranks traversal detects cycles among require/before/notify/subscribe (and containment) edges. The graph_cycle_handler marks every involved resource's status as failed ('resource is part of a dependency cycle') so the report is accurate, then raises Puppet::Error 'One or more resource dependency cycles detected in graph' and the run stops without syncing anything.","triggerScenarios":"Resources A, B, C where A requires B, B requires C, and C requires A; class containment loops (class A includes B while B requires something in A); anchor/contain patterns that add both directions; explicit `require => Class['x']` combined with `Class['x'] { require => Class['y'] }` where y ultimately points back. Exported resources from other nodes can close the loop invisibly.","commonSituations":"Profile/module refactors that add 'require => Class[base]' while base already contains the caller; anchor-based ordering (anchor -> class -> anchor); rings formed through exported resources (nagios_service <-> host edges); a single typo'd before/require pair after a manifest split.","solutions":["Run the agent with --graph and render the generated .dot files (graphviz dot -Tsvg *.dot) to see the cycle edges","Read the run report: every resource in the cycle has the status 'resource is part of a dependency cycle' naming exactly the members","Break the cycle by deleting one relationship edge, usually the redundant 'require => Class[...]' that containment already provides","Replace anchor-style bidirectional ordering with 'contain' + single-direction 'before'/'require'","For exported-resource rings, add staged/storeconfigs-friendly one-way edges instead of mutual notifies"],"exampleFix":"# before (cycle: a -> b -> a)\nfile { '/etc/app': require => File['/etc/app/conf.d'] }\nfile { '/etc/app/conf.d': require => File['/etc/app'] }\n\n# after (single direction)\nfile { '/etc/app': before => File['/etc/app/conf.d'] }\nfile { '/etc/app/conf.d': }","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"begin\n  apply_catalog(catalog)\nrescue Puppet::Error => e\n  raise if e.message !~ /dependency cycle/\n  report.resource_statuses.values\n       .select { |s| s.failed }\n       .each { |s| warn \"in cycle: #{s.resource}\" }\nend","preventionTips":["Compile catalogs in CI (rspec-puppet / puppet catalog compile) to catch cycles before agents do","Prefer contain + one-directional require over anchor/require pairs","Run with --graph when relationships grow and inspect the dot output","Avoid require => Class[...] on classes that contain the requiring resource"],"tags":["puppet","dependency-cycle","relationship-graph","catalog"],"backgroundTag":"circular-dependency","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}