puppetlabs/puppet · error · ArgumentError

%{option} before_action requires a block

Error message

%{option} before_action requires a block

What it means

Puppet::Interface::OptionBuilder#before_action, used inside `option ... do ... end` blocks in the Faces DSL, requires a block literal (lib/puppet/interface/option_builder.rb:47). Calling it with no block — a bare `before_action` — raises ArgumentError at face-load time. `%{option}` renders the Option being built, i.e. its dashed name such as --environment.

Source

Thrown at lib/puppet/interface/option_builder.rb:47

    unless private_method_defined? dsl
      define_method(dsl) do |value| @option.send(setter, value) end
    end
  end

  # Override some methods that deal in blocks, not objects.

  # Sets a block to be executed when an action is invoked before the
  # main action code. This is most commonly used to validate an option.
  # @yieldparam action [Puppet::Interface::Action] The action being
  #   invoked
  # @yieldparam args [Array] The arguments given to the action
  # @yieldparam options [Hash<Symbol=>Object>] Any options set
  # @api public
  # @dsl Faces
  def before_action(&block)
    unless block
      # TRANSLATORS 'before_action' is a method name and should not be translated
      raise ArgumentError, _("%{option} before_action requires a block") % { option: @option }
    end
    if @option.before_action
      # TRANSLATORS 'before_action' is a method name and should not be translated
      raise ArgumentError, _("%{option} already has a before_action set") % { option: @option }
    end
    unless block.arity == 3 then
      # TRANSLATORS 'before_action' is a method name and should not be translated
      raise ArgumentError, _("before_action takes three arguments, action, args, and options")
    end

    @option.before_action = block
  end

  # Sets a block to be executed after an action is invoked.
  # !(see before_action)
  # @api public
  # @dsl Faces
  def after_action(&block)

View on GitHub (pinned to e227c27540)

Solutions

  1. Always pass a literal block: `before_action { |action, args, options| ... }`
  2. To reuse a named method, wrap it: `before_action { |*a| my_validator(*a) }`
  3. If the hook is conditional, guard the whole call: `before_action { ... } if hook_needed`

Example fix

# before
option "--verbose" do
  before_action
end

# after
option "--verbose" do
  before_action do |action, args, options|
    raise "--verbose not supported here" if options[:verbose]
  end
end
Defensive patterns

Strategy: validation

Validate before calling

# when the hook comes from a variable
raise ArgumentError, 'before_action needs a block' if hook.nil?
before_action(&hook) # inside the option block

Prevention

When it happens

Trigger: `option '--foo' do before_action end` (no block); `before_action(&hook)` where hook is nil; intending to call a helper method literally named before_action.

Common situations: Refactoring a block body into a method and leaving a bare call; passing an optional hook variable that was never set; misunderstanding the DSL as method-reference style.

Related errors


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