puppetlabs/puppet · error · ArgumentError

before action hook for %{name} is a %{class_name}, not a pro

Error message

before action hook for %{name} is a %{class_name}, not a proc

What it means

Puppet::Interface::Option#before_action= registers a validation hook by sending `__add_method` to the parent face/action and refuses anything that is not a Proc (lib/puppet/interface/option.rb:158). The hook is invoked as `call(action, args, options)` when the action runs, so a Symbol or Method would never execute and fail silently. The DSL `before_action { |action, args, options| ... }` always supplies a proper block.

Source

Thrown at lib/puppet/interface/option.rb:158

    @default and @default.call
  end

  attr_reader :parent, :name, :aliases, :optparse, :required

  def required=(value)
    if has_default?
      raise ArgumentError, _("%{name} can't be optional and have a default value") % { name: self }
    end

    @required = value
  end

  attr_reader :before_action

  def before_action=(proc)
    unless proc.is_a? Proc
      # TRANSLATORS 'proc' is a Ruby block of code
      raise ArgumentError, _("before action hook for %{name} is a %{class_name}, not a proc") %
                           { name: self, class_name: proc.class.name.inspect }
    end
    @before_action =
      @parent.__send__(:__add_method, __decoration_name(:before), proc)
  end

  attr_reader :after_action

  def after_action=(proc)
    unless proc.is_a? Proc
      # TRANSLATORS 'proc' is a Ruby block of code
      raise ArgumentError, _("after action hook for %{name} is a %{class_name}, not a proc") %
                           { name: self, class_name: proc.class.name.inspect }
    end
    @after_action =
      @parent.__send__(:__add_method, __decoration_name(:after), proc)
  end

View on GitHub (pinned to e227c27540)

Solutions

  1. Pass a wrapping proc: `opt.before_action = proc { |action, args, options| validate(action, args, options) }`
  2. Convert Method objects with `.to_proc` (Ruby 2.7+) — the result is a real Proc
  3. Prefer the DSL `before_action` inside the option block

Example fix

# before
opt.before_action = :validate

# after
opt.before_action = proc { |action, args, options| validate(options) }
Defensive patterns

Strategy: type-guard

Type guard

def hook!(value, what)
  raise ArgumentError, "#{what} must be a Proc" unless value.is_a?(Proc)
  value
end

opt.before_action = hook!(candidate, 'before_action')

Prevention

When it happens

Trigger: `opt.before_action = :validate` (Symbol); `opt.before_action = method(:validate)` (Method is not a Proc); storing hook names from a config hash and assigning them directly.

Common situations: Refactoring DSL blocks into named methods; configuration-driven hook registration that records names instead of blocks.

Related errors


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