puppetlabs/puppet · error · ArgumentError
Given argument must be a Hash
Error message
Given argument must be a Hash
What it means
Puppet::InfoService::ClassInformationService#classes_per_environment expects a Hash mapping environment name(s) to a list of manifest files. Any other top-level type (Array, String, nil) raises ArgumentError immediately. This service backs 'puppet parser' / face APIs and PuppetDB/Puppet Server class-info queries, so callers marshalling parameters incorrectly (e.g., a bare file list) hit this guard.
Source
Thrown at lib/puppet/info_service/class_information_service.rb:19
# frozen_string_literal: true
require_relative '../../puppet'
require_relative '../../puppet/pops'
require_relative '../../puppet/pops/evaluator/json_strict_literal_evaluator'
class Puppet::InfoService::ClassInformationService
def initialize
@file_to_result = {}
@parser = Puppet::Pops::Parser::EvaluatingParser.new()
end
def classes_per_environment(env_file_hash)
# In this version of puppet there is only one way to parse manifests, as feature switches per environment
# are added or removed, this logic needs to change to compute the result per environment with the correct
# feature flags in effect.
unless env_file_hash.is_a?(Hash)
raise ArgumentError, _('Given argument must be a Hash')
end
result = {}
# for each environment
# for each file
# if file already processed, use last result or error
#
env_file_hash.each do |env, files|
env_result = result[env] = {}
files.each do |f|
env_result[f] = result_of(f)
end
end
result
end
privateView on GitHub (pinned to e227c27540)
Solutions
- Pass a Hash of environment => file list: { 'production' => ['/etc/puppetlabs/code/environments/production/site.pp'] }
- If you have a flat file list, wrap it: files.each_with_object({}) { |f,h| (h['production'] ||= []) << f }
- Add a type check at the caller boundary and log the received class for debugging
- See the face/tool docs for classes_per_environment for the canonical payload shape
Example fix
# before svc.classes_per_environment(['/etc/puppetlabs/code/site.pp']) # => ArgumentError: Given argument must be a Hash # after svc.classes_per_environment( 'production' => ['/etc/puppetlabs/code/environments/production/site.pp'] )
Defensive patterns
Strategy: type-guard
Validate before calling
raise ArgumentError, "expected Hash, got #{arg.class}" unless arg.is_a?(Hash)
arg.each_value { |files| raise ArgumentError, 'values must be file lists' unless files.is_a?(Array) } Type guard
def env_file_hash?(arg)
arg.is_a?(Hash) && arg.values.all? { |v| v.is_a?(Array) && v.all? { |f| f.is_a?(String) } }
end Prevention
- Document the {env => [files]} shape at every API boundary and validate at ingress
- Write a small contract test that feeds wrong shapes and asserts ArgumentError
- When forwarding JSON payloads, validate with a schema before calling the service
When it happens
Trigger: Calling Puppet::InfoService::ClassInformationService.new.classes_per_environment(['/path/site.pp']) or (nil) or ('production') instead of { 'production' => ['/path/site.pp'] }; JSON API wrappers that unwrap the payload one level too many and forward an array.
Common situations: Custom tooling or puppetserver routing wrappers that construct the argument from JSON and assume a list; refactorings that change the service signature's shape; exploratory scripts iterating files directly.
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
- Please provide a file or checksum to diff with
- Failed to diff files
- Unsupported translation file format #{file_format}; please u
- Unknown service #{name}
- Not a valid full name: %{full_module_name}
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/97ac5ad63c71a2f6.
Report an issue: GitHub.