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
- Use the reported first-declaration location to find and remove/rename one of the two declarations
- Namespace titles in defined-type-heavy code: file { "${name}-config": } instead of fixed titles
- Use stdlib ensure_resource('file', '/tmp/x', {...}) for idempotent 'declare once' semantics across classes
- 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
- Avoid fixed global titles in classes; namespace titles with define parameters
- Use stdlib ensure_resource()/ensure_resources() for cross-class 'declare once' resources
- Keep exactly one version of each module on the modulepath (check `puppet module list --tree`)
- Run `puppet parser validate` and catalog previews in CI to catch collisions before agent runs
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
- No title provided and %{type} is not a valid resource refere
- Unable to verify the SSL certificate at %{uri}
- Could not find resource '%{res}' in parameter '%{param}'
- Could not deserialize catalog from %{format}: %{detail}
- Resource instance does not match request key
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/839bb0f699518d76.
Report an issue: GitHub.