{"record":{"id":"e14d53cff9b7f3d3","repo":"puppetlabs/puppet","slug":"fileset-recurse-parameter-must-not-be-a-number-any","errorCode":null,"errorMessage":"Fileset recurse parameter must not be a number anymore, please use recurselimit","messagePattern":"Fileset recurse parameter must not be a number anymore, please use recurselimit","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/file_serving/fileset.rb","lineNumber":55,"sourceCode":"\n    @path = path\n\n    # Set our defaults.\n    self.ignore = []\n    self.links = :manage\n    @recurse = false\n    @recurselimit = :infinite\n    @max_files = 0\n\n    if options.is_a?(Puppet::Indirector::Request)\n      initialize_from_request(options)\n    else\n      initialize_from_hash(options)\n    end\n\n    raise ArgumentError, _(\"Fileset paths must exist\") unless valid?(path)\n    # TRANSLATORS \"recurse\" and \"recurselimit\" are parameter names and should not be translated\n    raise ArgumentError, _(\"Fileset recurse parameter must not be a number anymore, please use recurselimit\") if @recurse.is_a?(Integer)\n  end\n\n  # Return a list of all files in our fileset.  This is different from the\n  # normal definition of find in that we support specific levels\n  # of recursion, which means we need to know when we're going another\n  # level deep, which Find doesn't do.\n  def files\n    files = perform_recursion\n    soft_max_files = 1000\n\n    # munged_max_files is needed since puppet http handler is keeping negative numbers as strings\n    # https://github.com/puppetlabs/puppet/blob/main/lib/puppet/network/http/handler.rb#L196-L197\n    munged_max_files = max_files == '-1' ? -1 : max_files\n\n    if munged_max_files > 0 && files.size > munged_max_files\n      raise Puppet::Error, _(\"The directory '%{path}' contains %{entries} entries, which exceeds the limit of %{munged_max_files} specified by the max_files parameter for this resource. The limit may be increased, but be aware that large number of file resources can result in excessive resource consumption and degraded performance. Consider using an alternate method to manage large directory trees\") % { path: path, entries: files.size, munged_max_files: munged_max_files }\n    elsif munged_max_files == 0 && files.size > soft_max_files\n      Puppet.warning _(\"The directory '%{path}' contains %{entries} entries, which exceeds the default soft limit %{soft_max_files} and may cause excessive resource consumption and degraded performance. To remove this warning set a value for `max_files` parameter or consider using an alternate method to manage large directory trees\") % { path: path, entries: files.size, soft_max_files: soft_max_files }","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/file_serving/fileset.rb#L37-L73","documentation":"Historically (pre-2.6) Puppet allowed `recurse => <integer>` to mean 'recurse N levels deep'. Modern Fileset keeps depth in recurselimit and expects recurse to be boolean; if the options hash leaves @recurse as an Integer, initialize raises ArgumentError with a migration hint. This is a deliberate API-compatibility tripwire, not a runtime failure.","triggerScenarios":"Passing `Puppet::FileServing::Fileset.new('/x', recurse: 2)`; reusing legacy manifests or serialized option hashes written for Puppet < 2.6 that carry numeric recurse values; forwarding unfiltered request options into a Fileset.","commonSituations":"Very old module code or snippets copied from ancient wiki/blog posts; tools that deserialize stored fileset options from an old Puppet version; migrations off Puppet 0.2x/2.x infrastructure.","solutions":["Replace the numeric recurse with `recurse => true` (or `virtual`) plus `recurselimit => N` at the same depth","If you feed option hashes from external sources, munge integer recurse into recurse/recurselimit before constructing the Fileset","Search manifests and data for `recurse\\s*=>\\s*[0-9]` to find all occurrences"],"exampleFix":"# before (manifest)\nfile { '/etc/app':\n  ensure  => directory,\n  source  => 'puppet:///modules/app/etc',\n  recurse => 1,\n}\n\n# after\nfile { '/etc/app':\n  ensure        => directory,\n  source        => 'puppet:///modules/app/etc',\n  recurse       => true,\n  recurselimit  => 1,\n}","handlingStrategy":"validation","validationCode":"# Munge legacy integer recurse before constructing a Fileset\ndef munge_recurse(opts)\n  opts = opts.dup\n  if opts[:recurse].is_a?(Integer)\n    opts[:recurselimit] = opts.delete(:recurse)\n    opts[:recurse] = true\n  end\n  opts\nend","typeGuard":"# Detect the deprecated shape before it reaches Puppet\ndef legacy_recurse?(opts)\n  opts.is_a?(Hash) && opts[:recurse].is_a?(Integer)\nend","tryCatchPattern":"begin\n  Puppet::FileServing::Fileset.new(path, opts)\nrescue ArgumentError => e\n  raise unless e.message.include?('recurse parameter must not be a number')\n  opts = { recurse: true, recurselimit: opts[:recurse] }\n  retry\nend","preventionTips":["Grep manifests for `recurse\\s*=>\\s*\\d` during Puppet upgrades","Treat recurse as boolean in all new code; depth belongs in recurselimit","Sanitize option hashes deserialized from old stored data"],"tags":["puppet","fileset","deprecated-parameter","recursion","argumenterror"],"backgroundTag":"deprecated-parameter-usage","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}