puppetlabs/puppet · error · Puppet::ExecutionFailure

output.to_s

Error message

output.to_s

What it means

Puppet::Util.execute runs a command with stdout and stderr combined under a C locale (the POSIX pipe path) and, when the failonfail option is true (the default), raises Puppet::ExecutionFailure if the exit status is non-zero. The exception message is simply the command's captured output (output.to_s). This is Puppet's standard mechanism for surfacing a failing external command.

Source

Thrown at lib/puppet/util/execution.rb:88

      debug "Executing '#{command_str}'"
    else
      Puppet.debug { "Executing '#{command_str}'" }
    end

    # force the run of the command with
    # the user/system locale to "C" (via environment variables LANG and LC_*)
    # it enables to have non localized output for some commands and therefore
    # a predictable output
    english_env = ENV.to_hash.merge({ 'LANG' => 'C', 'LC_ALL' => 'C' })
    output = Puppet::Util.withenv(english_env) do
      # We are intentionally using 'pipe' with open to launch a process
      open("| #{command_str} 2>&1") do |pipe| # rubocop:disable Security/Open
        yield pipe
      end
    end

    if failonfail && exitstatus != 0
      raise Puppet::ExecutionFailure, output.to_s
    end

    output
  end

  def self.exitstatus
    $CHILD_STATUS.exitstatus
  end
  private_class_method :exitstatus

  # Default empty options for {execute}
  NoOptionsSpecified = {}

  # Executes the desired command, and return the status and output.
  # def execute(command, options)
  # @param command [Array<String>, String] the command to execute. If it is
  #   an Array the first element should be the executable and the rest of the
  #   elements should be the individual arguments to that executable.

View on GitHub (pinned to e227c27540)

Solutions

  1. Re-run the failing command manually as the same user with the same environment; the real error is in the exception message (the combined output)
  2. Pass failonfail: false when a non-zero exit is expected, and branch on the returned output or exit status instead
  3. Rescue Puppet::ExecutionFailure explicitly at the call site and handle it
  4. Control the environment (PATH, locale) via the execute options so the intended command runs

Example fix

# before
Puppet::Util.execute(['/usr/sbin/useradd', '-m', 'bob'])
# => Puppet::ExecutionFailure: useradd: user 'bob' already exists

# after
begin
  Puppet::Util.execute(['/usr/sbin/useradd', '-m', 'bob'])
rescue Puppet::ExecutionFailure => e
  raise unless e.message.include?('already exists')
end
Defensive patterns

Strategy: try-catch

Validate before calling

unless Puppet::Util.which('systemctl')
  raise ArgumentError, 'systemctl not found on PATH'
end
Puppet::Util.execute(['systemctl', 'is-active', 'nginx'], failonfail: false)

Try / catch

begin
  Puppet::Util.execute(cmd)
rescue Puppet::ExecutionFailure => e
  Puppet.err("Command failed: #{e.message}")
  raise unless e.message.include?('already exists')
end

Prevention

When it happens

Trigger: Calling Puppet::Util.execute('systemctl reload nginx') (failonfail defaults to true) where the command exists but exits non-zero: a package manager returning 100, a validation command rejecting input, a service control command failing, and so on.

Common situations: Providers and exec-style code invoking CLI tools that fail on bad input, commands behaving differently as root versus another user, PATH differences causing a different binary to run, and callers that do not expect any non-zero exit.

Related errors


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