puppetlabs/puppet · error · ArgumentError
%{option} already has a before_action set
Error message
%{option} already has a before_action set What it means
OptionBuilder#before_action allows exactly one hook per option: if `@option.before_action` is already set, a second call raises (lib/puppet/interface/option_builder.rb:51). Faces have no hook-chaining semantics, so the duplicate declaration is rejected instead of merged. The error surfaces when the face file is loaded.
Source
Thrown at lib/puppet/interface/option_builder.rb:51
# 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)
unless block
# TRANSLATORS 'after_action' is a method name and should not be translated
raise ArgumentError, _("%{option} after_action requires a block") % { option: @option }
endView on GitHub (pinned to e227c27540)
Solutions
- Merge both bodies into a single `before_action` block
- Delete the stale hook after moving validation elsewhere
- If two concerns are distinct, keep one in before_action and move the other to after_action (different timing, different semantics)
Example fix
# before
option "--foo" do
before_action { |a, args, opts| check_a(opts) }
before_action { |a, args, opts| check_b(opts) }
end
# after
option "--foo" do
before_action do |a, args, opts|
check_a(opts)
check_b(opts)
end
end Defensive patterns
Strategy: validation
Validate before calling
# before registering a hook on an Option object
raise ArgumentError, 'before_action already set' if opt.before_action
opt.before_action = proc { |action, args, options| check(options) } Prevention
- One before_action per option; merge additional checks into the existing block
- Grep option blocks for repeated `before_action` before committing
- Watch mixins and helpers that register hooks — they can double-register when called twice
When it happens
Trigger: Two `before_action` blocks in the same option block; a helper invoked from the option block that itself calls before_action; iterating over validation rules and registering the hook twice.
Common situations: Copy-pasted option blocks that both carry hooks; refactoring that moves a hook but leaves the original behind; mixin modules contributing an extra hook.
Related errors
- %{option} already has an after_action set
- %{option} already has a default value
- %{name} can't be optional and have a default value
- default value for %{name} is a %{class_name}, not a proc
- before action hook for %{name} is a %{class_name}, not a pro
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/61df14cb2b240f79.
Report an issue: GitHub.