puppetlabs/puppet · error · Puppet::Error
One or more resource dependency cycles detected in graph
Error message
One or more resource dependency cycles detected in graph
What it means
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.
Source
Thrown at lib/puppet/transaction.rb:170
Puppet.log_exception(detail, _("post_resource_eval failed for provider %{provider}") % { provider: provider })
end
persistence.save if persistence.enabled?(catalog)
end
# Graph cycles are returned as an array of arrays
# - outer array is an array of cycles
# - each inner array is an array of resources involved in a cycle
# Short circuit resource evaluation if we detect cycle(s) in the graph. Mark
# each corresponding resource as failed in the report before we fail to
# ensure accurate reporting.
graph_cycle_handler = lambda do |cycles|
cycles.flatten.uniq.each do |resource|
# We add a failed resource event to the status to ensure accurate
# reporting through the event manager.
resource_status(resource).fail_with_event(_('resource is part of a dependency cycle'))
end
raise Puppet::Error, _('One or more resource dependency cycles detected in graph')
end
# Generate the relationship graph, set up our generator to use it
# for eval_generate, then kick off our traversal.
generator.relationship_graph = relationship_graph
progress = 0
relationship_graph.traverse(:while => continue_while,
:pre_process => pre_process,
:overly_deferred_resource_handler => overly_deferred_resource_handler,
:canceled_resource_handler => canceled_resource_handler,
:graph_cycle_handler => graph_cycle_handler,
:teardown => teardown) do |resource|
progress += 1
if resource.is_a?(Puppet::Type::Component)
Puppet.warning _("Somehow left a component in the relationship graph")
else
if Puppet[:evaltrace] && @catalog.host_config?
resource.info _("Starting to evaluate the resource (%{progress} of %{total})") % { progress: progress, total: relationship_graph.size }View on GitHub (pinned to e227c27540)
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
Example fix
# before (cycle: a -> b -> a)
file { '/etc/app': require => File['/etc/app/conf.d'] }
file { '/etc/app/conf.d': require => File['/etc/app'] }
# after (single direction)
file { '/etc/app': before => File['/etc/app/conf.d'] }
file { '/etc/app/conf.d': } Defensive patterns
Strategy: try-catch
Try / catch
begin
apply_catalog(catalog)
rescue Puppet::Error => e
raise if e.message !~ /dependency cycle/
report.resource_statuses.values
.select { |s| s.failed }
.each { |s| warn "in cycle: #{s.resource}" }
end Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unable to verify the SSL certificate at %{uri}
- No title provided and %{type} is not a valid resource refere
- Could not find resource '%{res}' in parameter '%{param}'
- Duplicate declaration: %{resource} is already declared; cann
- Could not deserialize catalog from %{format}: %{detail}
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/5077cf2a1de346b7.
Report an issue: GitHub.