{"record":{"id":"839bb0f699518d76","repo":"puppetlabs/puppet","slug":"duplicate-declaration-resource-is-already-decl","errorCode":null,"errorMessage":"Duplicate declaration: %{resource} is already declared; cannot redeclare","messagePattern":"Duplicate declaration: %(.+?) is already declared; cannot redeclare","errorType":"exception","errorClass":"Puppet::Resource::Catalog::DuplicateResourceError","httpStatus":null,"severity":"error","filePath":"lib/puppet/resource/catalog.rb","lineNumber":586,"sourceCode":"    transaction.for_network_device = Puppet.lookup(:network_device) { nil } || options[:network_device]\n\n    transaction\n  end\n\n  # Verify that the given resource isn't declared elsewhere.\n  def fail_on_duplicate_type_and_title(resource, title_key)\n    # Short-circuit the common case,\n    existing_resource = @resource_table[title_key]\n    return unless existing_resource\n\n    # If we've gotten this far, it's a real conflict\n    error_location_str = Puppet::Util::Errors.error_location(existing_resource.file, existing_resource.line)\n    msg = if error_location_str.empty?\n            _(\"Duplicate declaration: %{resource} is already declared; cannot redeclare\") % { resource: resource.ref }\n          else\n            _(\"Duplicate declaration: %{resource} is already declared at %{error_location}; cannot redeclare\") % { resource: resource.ref, error_location: error_location_str }\n          end\n    raise DuplicateResourceError.new(msg, resource.file, resource.line)\n  end\n\n  # An abstracted method for converting one catalog into another type of catalog.\n  # This pretty much just converts all of the resources from one class to another, using\n  # a conversion method.\n  def to_catalog(convert)\n    result = self.class.new(name, environment_instance)\n\n    result.version = version\n    result.code_id = code_id\n    result.catalog_uuid = catalog_uuid\n    result.catalog_format = catalog_format\n    result.metadata = metadata\n    result.recursive_metadata = recursive_metadata\n\n    map = {}\n    resources.each do |resource|\n      next if virtual_not_exported?(resource)","sourceCodeStart":568,"sourceCodeEnd":604,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/resource/catalog.rb#L568-L604","documentation":"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}).","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"# before\n# class a: file { '/tmp/x': ensure => file }\n# class b: file { '/tmp/x': ensure => file }   # node includes both -> DuplicateResourceError\n# after\n# class b uses stdlib instead of redeclaring:\nensure_resource('file', '/tmp/x', { 'ensure' => 'file' })","handlingStrategy":"validation","validationCode":"# Ruby: pre-flight a manifest pair for colliding declarations\nrefs = manifest_a_resources + manifest_b_resources   # e.g. from Puppet::Parser::Parser or catalog introspection\ndups = refs.group_by(&:ref).select { |_, v| v.size > 1 }\nraise \"duplicate declarations: #{dups.keys.join(', ')}\" unless dups.empty?","typeGuard":null,"tryCatchPattern":"begin\n  catalog = compiler.compile\nrescue Puppet::Resource::Catalog::DuplicateResourceError => e\n  # message contains the first declaration's file:line — use it to locate the original\n  puts e.message\n  raise\nend","preventionTips":["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"],"tags":["puppet","catalog","duplicate-declaration","compile-error","resource"],"backgroundTag":"duplicate-resource-declaration","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}