puppetlabs/puppet · error · ArgumentError

Command must be a String or Array<String>, got value of clas

Error message

Command must be a String or Array<String>, got value of class %{klass}

What it means

The exec type's command parameter validates that its value is a String or an Array (of strings for parameterized invocation). Any other class — most commonly nil from an undef variable, or a Hash — raises ArgumentError 'Command must be a String or Array<String>'. This happens at resource validation time, before any execution attempt.

Source

Thrown at lib/puppet/type/exec.rb:218

        succeeds, any output produced will be logged at the instance's
        normal log level (usually `notice`), but if the command fails
        (meaning its return code does not match the specified code) then
        any output is logged at the `err` log level.

        Multiple `exec` resources can use the same `command` value; Puppet
        only uses the resource title to ensure `exec`s are unique.

        On *nix platforms, the command can be specified as an array of
        strings and Puppet will invoke it using the more secure method of
        parameterized system calls. For example, rather than executing the
        malicious injected code, this command will echo it out:

            command => ['/bin/echo', 'hello world; rm -rf /']
      "

      validate do |command|
        unless command.is_a?(String) || command.is_a?(Array)
          raise ArgumentError, _("Command must be a String or Array<String>, got value of class %{klass}") % { klass: command.class }
        end
      end
    end

    newparam(:path) do
      desc "The search path used for command execution.
        Commands must be fully qualified if no path is specified.  Paths
        can be specified as an array or as a '#{File::PATH_SEPARATOR}' separated list."

      # Support both arrays and colon-separated fields.
      def value=(*values)
        @value = values.flatten.collect { |val|
          val.split(File::PATH_SEPARATOR)
        }.flatten
      end
    end

    newparam(:user) do

View on GitHub (pinned to e227c27540)

Solutions

  1. Default the variable: $cmd = pick($cmd, '/bin/true') or set a hiera default_value
  2. Verify the data path: puppet lookup --node <n> profile::exec::command --explain shows which layer failed to supply it
  3. Conditionally declare the exec only when the command is set (if $cmd { exec { ... } })
  4. Ensure the value is a plain string, not structured data

Example fix

# before
exec { 'run-migration': command => $migration_cmd } # undef => ArgumentError

# after
$migration_cmd = pick(lookup('app::migration_cmd', { 'default_value' => undef }), '/bin/true')
exec { 'run-migration': command => $migration_cmd }
Defensive patterns

Strategy: validation

Validate before calling

$cmd = pick($cmd, '/bin/true')
# or in Ruby:
raise ArgumentError, 'command is required' unless command.is_a?(String) || command.is_a?(Array)

Prevention

When it happens

Trigger: exec { 'x': command => $cmd } where $cmd is undef (hiera key missing, variable from a class not evaluated); command built by template interpolation that returned nil; passing a Hash from a lookup; arrays containing nil elements are not caught here but fail later at execution.

Common situations: Profile data refactors where a profile silently stops setting a variable an exec depends on; deep hiera merges shadowing a key with undef; node groups missing a classification layer; optional execs whose enabling class was renamed.

Related errors


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