{"record":{"id":"61df14cb2b240f79","repo":"puppetlabs/puppet","slug":"option-already-has-a-before-action-set","errorCode":null,"errorMessage":"%{option} already has a before_action set","messagePattern":"%(.+?) already has a before_action set","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/interface/option_builder.rb","lineNumber":51,"sourceCode":"\n  # Override some methods that deal in blocks, not objects.\n\n  # Sets a block to be executed when an action is invoked before the\n  # main action code. This is most commonly used to validate an option.\n  # @yieldparam action [Puppet::Interface::Action] The action being\n  #   invoked\n  # @yieldparam args [Array] The arguments given to the action\n  # @yieldparam options [Hash<Symbol=>Object>] Any options set\n  # @api public\n  # @dsl Faces\n  def before_action(&block)\n    unless block\n      # TRANSLATORS 'before_action' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} before_action requires a block\") % { option: @option }\n    end\n    if @option.before_action\n      # TRANSLATORS 'before_action' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} already has a before_action set\") % { option: @option }\n    end\n    unless block.arity == 3 then\n      # TRANSLATORS 'before_action' is a method name and should not be translated\n      raise ArgumentError, _(\"before_action takes three arguments, action, args, and options\")\n    end\n\n    @option.before_action = block\n  end\n\n  # Sets a block to be executed after an action is invoked.\n  # !(see before_action)\n  # @api public\n  # @dsl Faces\n  def after_action(&block)\n    unless block\n      # TRANSLATORS 'after_action' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} after_action requires a block\") % { option: @option }\n    end","sourceCodeStart":33,"sourceCodeEnd":69,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/interface/option_builder.rb#L33-L69","documentation":"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.","triggerScenarios":"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.","commonSituations":"Copy-pasted option blocks that both carry hooks; refactoring that moves a hook but leaves the original behind; mixin modules contributing an extra hook.","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)"],"exampleFix":"# before\noption \"--foo\" do\n  before_action { |a, args, opts| check_a(opts) }\n  before_action { |a, args, opts| check_b(opts) }\nend\n\n# after\noption \"--foo\" do\n  before_action do |a, args, opts|\n    check_a(opts)\n    check_b(opts)\n  end\nend","handlingStrategy":"validation","validationCode":"# before registering a hook on an Option object\nraise ArgumentError, 'before_action already set' if opt.before_action\nopt.before_action = proc { |action, args, options| check(options) }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["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"],"tags":["puppet","faces","dsl","duplicate","argumenterror"],"backgroundTag":"duplicate-declaration","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}