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
endView on GitHub (pinned to e227c27540)
Solutions
- Pass the libraries as strings: libs: %w[selinux] or libs: ['selinux']
- Coerce defensively when the list comes from config: Array(libs).map(&:to_s)
- 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
- Always use %w[...] string arrays for :libs
- Whitelist with an all?(String) predicate when the library list comes from config
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
- Puppet #{Puppet.version} requires Ruby #{Puppet::OLDEST_RECO
- Invalid entry at %{error_location}: '%{file_text}'
- %{mount} is already mounted at %{name} at %{error_location}
- Fileset paths must be fully qualified: %{path}
- Fileset paths must exist
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/d0440b9d2c22a022.
Report an issue: GitHub.