puppetlabs/puppet · error · Puppet::Resource::Catalog::DuplicateResourceError

Duplicate declaration: %{resource} is already declared; cann

Error message

Duplicate declaration: %{resource} is already declared; cannot redeclare

What it means

Catalog#fail_on_duplicate_type_and_title (lib/puppet/resource/catalog.rb:586) fires when a resource being added has the same type + title as one already in @resource_table — Puppet forbids declaring the same resource twice in one catalog because ownership, ordering and overrides become ambiguous. It raises Puppet::Resource::Catalog::DuplicateResourceError, including the file:line of the FIRST declaration when known (the alternate message with %{error_location}).

Source

Thrown at lib/puppet/resource/catalog.rb:586

    transaction.for_network_device = Puppet.lookup(:network_device) { nil } || options[:network_device]

    transaction
  end

  # Verify that the given resource isn't declared elsewhere.
  def fail_on_duplicate_type_and_title(resource, title_key)
    # Short-circuit the common case,
    existing_resource = @resource_table[title_key]
    return unless existing_resource

    # If we've gotten this far, it's a real conflict
    error_location_str = Puppet::Util::Errors.error_location(existing_resource.file, existing_resource.line)
    msg = if error_location_str.empty?
            _("Duplicate declaration: %{resource} is already declared; cannot redeclare") % { resource: resource.ref }
          else
            _("Duplicate declaration: %{resource} is already declared at %{error_location}; cannot redeclare") % { resource: resource.ref, error_location: error_location_str }
          end
    raise DuplicateResourceError.new(msg, resource.file, resource.line)
  end

  # An abstracted method for converting one catalog into another type of catalog.
  # This pretty much just converts all of the resources from one class to another, using
  # a conversion method.
  def to_catalog(convert)
    result = self.class.new(name, environment_instance)

    result.version = version
    result.code_id = code_id
    result.catalog_uuid = catalog_uuid
    result.catalog_format = catalog_format
    result.metadata = metadata
    result.recursive_metadata = recursive_metadata

    map = {}
    resources.each do |resource|
      next if virtual_not_exported?(resource)

View on GitHub (pinned to e227c27540)

Solutions

  1. Use the reported first-declaration location to find and remove/rename one of the two declarations
  2. Namespace titles in defined-type-heavy code: file { "${name}-config": } instead of fixed titles
  3. Use stdlib ensure_resource('file', '/tmp/x', {...}) for idempotent 'declare once' semantics across classes
  4. Check `puppet module list` / modulepath for shadowed duplicate module versions

Example fix

# before
# class a: file { '/tmp/x': ensure => file }
# class b: file { '/tmp/x': ensure => file }   # node includes both -> DuplicateResourceError
# after
# class b uses stdlib instead of redeclaring:
ensure_resource('file', '/tmp/x', { 'ensure' => 'file' })
Defensive patterns

Strategy: validation

Validate before calling

# Ruby: pre-flight a manifest pair for colliding declarations
refs = manifest_a_resources + manifest_b_resources   # e.g. from Puppet::Parser::Parser or catalog introspection
dups = refs.group_by(&:ref).select { |_, v| v.size > 1 }
raise "duplicate declarations: #{dups.keys.join(', ')}" unless dups.empty?

Try / catch

begin
  catalog = compiler.compile
rescue Puppet::Resource::Catalog::DuplicateResourceError => e
  # message contains the first declaration's file:line — use it to locate the original
  puts e.message
  raise
end

Prevention

When it happens

Trigger: Two `file { '/tmp/x': }` declarations reachable from the same node's catalog (e.g. in site.pp and in an included class); a class included from two places that declares a non-namespaced resource; two versions of the same module on the modulepath both declaring the resource; generated code (define loops) producing colliding titles.

Common situations: Class A and class B both declare `user { 'deploy': }` and a node includes both; resources with interpolated titles colliding (`file { "/etc/${app}": }` where $app repeats); duplicate module directories in modulepath (old copy shadowing new); refactors that moved a resource into a shared class while the original declaration remained.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/839bb0f699518d76. Report an issue: GitHub.