puppetlabs/puppet · error · ArgumentError

Libraries must be passed as strings not %{klass}

Error message

Libraries must be passed as strings not %{klass}

What it means

Puppet::Util::Feature#add declares an optional capability and tests it by require-ing each library listed in :libs. load_library enforces that every entry is a String (it is passed straight to Kernel#require); a Symbol or any other class raises ArgumentError naming the offending class. The feature test never runs when this fires.

Source

Thrown at lib/puppet/util/feature.rb:110

        result = nil
      end
      @results[name] = result
      result
    else
      libs = options[:libs]
      if libs
        libs = [libs] unless libs.is_a?(Array)
        libs.all? { |lib| load_library(lib, name) } ? true : nil
      else
        true
      end
    end
  end

  private

  def load_library(lib, name)
    raise ArgumentError, _("Libraries must be passed as strings not %{klass}") % { klass: lib.class } unless lib.is_a?(String)

    @rubygems ||= Puppet::Util::RubyGems::Source.new
    @rubygems.clear_paths

    begin
      require lib
      true
    rescue LoadError
      # Expected case. Required library insn't installed.
      debug_once(_("Could not find library '%{lib}' required to enable feature '%{name}'") %
        { lib: lib, name: name })
      false
    rescue StandardError, ScriptError => detail
      debug_once(_("Exception occurred while loading library '%{lib}' required to enable feature '%{name}': %{detail}") %
        { lib: lib, name: name, detail: detail })
      false
    end
  end

View on GitHub (pinned to e227c27540)

Solutions

  1. Pass the libraries as strings: libs: %w[selinux] or libs: ['selinux']
  2. Coerce defensively when the list comes from config: Array(libs).map(&:to_s)
  3. For a single library a bare string is fine; Feature#add wraps non-Array values in an array automatically

Example fix

# before
Puppet.features.add(:my_feature, libs: [:my_lib])
# => ArgumentError: Libraries must be passed as strings not Symbol

# after
Puppet.features.add(:my_feature, libs: %w[my_lib])
Defensive patterns

Strategy: type-guard

Validate before calling

libs = Array(raw_libs).map { |lib| lib.is_a?(String) ? lib : lib.to_s }
Puppet.features.add(:my_feature, libs: libs)

Type guard

def string_libs?(libs)
  Array(libs).all? { |lib| lib.is_a?(String) }
end

Prevention

When it happens

Trigger: Calling Puppet.features.add(:my_feature, libs: [:my_lib]) or libs: :my_lib — symbols instead of strings — or passing constants/objects in the libs array. Only strings are require-able, so anything else is rejected up front.

Common situations: Custom extensions and modules registering features using symbol lists (a natural style slip in Puppet code, where so many APIs take symbols), and code migrated from older examples with different conventions.

Understand the failure class

Background: Invalid argument type errors: "must be of type string", "expected X, got Y", and ERR_INVALID_ARG_TYPE explained — this error's family across 15 libraries.

Related errors


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