{"record":{"id":"1a564a96d18f7e32","repo":"puppetlabs/puppet","slug":"given-variables-must-be-a-hash-got-type","errorCode":null,"errorMessage":"Given variables must be a hash, got %{type}","messagePattern":"Given variables must be a hash, got %(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pal/pal_impl.rb","lineNumber":378,"sourceCode":"  # speeded up (as getting a fresh set of facts is avoided in a later step).\n  #\n  def self.prepare_node_facts(node, facts)\n    # Prepare the node with facts if it does not already have them\n    if node.facts.nil?\n      node_facts = facts.nil? ? nil : Puppet::Node::Facts.new(Puppet[:node_name_value], facts)\n      node.fact_merge(node_facts)\n      # Add server facts so $server_facts[environment] exists when doing a puppet script\n      # SCRIPT TODO: May be needed when running scripts under orchestrator. Leave it for now.\n      #\n      node.add_server_facts({})\n    end\n  end\n  private_class_method :prepare_node_facts\n\n  def self.add_variables(scope, variables)\n    return if variables.nil?\n    unless variables.is_a?(Hash)\n      raise ArgumentError, _(\"Given variables must be a hash, got %{type}\") % { type: variables.class }\n    end\n\n    rich_data_t = Puppet::Pops::Types::TypeFactory.rich_data\n    variables.each_pair do |k, v|\n      unless k =~ Puppet::Pops::Patterns::VAR_NAME\n        raise ArgumentError, _(\"Given variable '%{varname}' has illegal name\") % { varname: k }\n      end\n\n      unless rich_data_t.instance?(v)\n        raise ArgumentError, _(\"Given value for '%{varname}' has illegal type - got: %{type}\") % { varname: k, type: v.class }\n      end\n\n      scope.setvar(k, v)\n    end\n  end\n  private_class_method :add_variables\n\n  # The main routine for script compiler","sourceCodeStart":360,"sourceCodeEnd":396,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pal/pal_impl.rb#L360-L396","documentation":"PAL's add_variables injects caller-supplied variables into the evaluation scope. nil is a valid no-op, but any other non-Hash value raises ArgumentError reporting the actual class. The variables hash flows from the PAL entry points (variables: / target_variables: options) into the scope.","triggerScenarios":"variables: 'a=1' (String), variables: [['x', 1]] (array of pairs), or feeding JSON.parse output that is an Array/String directly to with_script_compiler/in_environment variables option.","commonSituations":"Passing serialized config (JSON/YAML fragments) without converting; .to_s applied to a hash; older tooling that passed key/value lists.","solutions":["Pass a Ruby Hash: variables: { 'debug' => true }","Convert pair arrays with .to_h or Hash[...] before the call","For JSON input, ensure JSON.parse returned an object ({}), not a scalar/array"],"exampleFix":"# before\nPuppet::Pal.with_script_compiler(variables: config.to_s) { |c| }\n\n# after\nPuppet::Pal.with_script_compiler(variables: config.to_h) { |c| }","handlingStrategy":"type-guard","validationCode":"raise ArgumentError, \"variables must be a Hash, got #{variables.class}\" unless variables.is_a?(Hash)","typeGuard":"def pal_variables?(v)\n  v.nil? || v.is_a?(Hash)\nend","tryCatchPattern":null,"preventionTips":["Convert JSON input with JSON.parse(x, object_class: Hash) and verify it is a Hash","Never call .to_s on a hash you intend to pass as variables"],"tags":["puppet","pal","ruby","argument-error","variables"],"backgroundTag":"invalid-argument-type","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}