{"record":{"id":"5abb447bcf936603","repo":"puppetlabs/puppet","slug":"option-after-action-requires-a-block","errorCode":null,"errorMessage":"%{option} after_action requires a block","messagePattern":"%(.+?) after_action requires a block","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/interface/option_builder.rb","lineNumber":68,"sourceCode":"      # 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\n    if @option.after_action\n      # TRANSLATORS 'after_action' is a method name and should not be translated\n      raise ArgumentError, _(\"%{option} already has an after_action set\") % { option: @option }\n    end\n    unless block.arity == 3 then\n      # TRANSLATORS 'after_action' is a method name and should not be translated\n      raise ArgumentError, _(\"after_action takes three arguments, action, args, and options\")\n    end\n\n    @option.after_action = block\n  end\n\n  # Sets whether the option is required. If no argument is given it\n  # defaults to setting it as a required option.\n  # @api public\n  # @dsl Faces\n  def required(value = true)","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/interface/option_builder.rb#L50-L86","documentation":"OptionBuilder#after_action, the post-invocation counterpart of before_action, equally requires a block literal (lib/puppet/interface/option_builder.rb:68). A bare `after_action` call inside an option block raises ArgumentError at face-load time with the option name interpolated. The hook runs after the action body completes.","triggerScenarios":"`option '--foo' do after_action end` (no block); `after_action(&hook)` where hook is nil.","commonSituations":"Symmetrical refactoring where before_action got a block but after_action was left as a placeholder; optional cleanup hooks that were never assigned.","solutions":["Always pass a literal block: `after_action { |action, args, options| ... }`","Wrap named methods: `after_action { |*a| cleanup(*a) }`","Guard conditional registration: `after_action { ... } if cleanup_needed`"],"exampleFix":"# before\noption \"--verbose\" do\n  after_action\nend\n\n# after\noption \"--verbose\" do\n  after_action do |action, args, options|\n    log_completion(action, options)\n  end\nend","handlingStrategy":"validation","validationCode":"# when the hook comes from a variable\nraise ArgumentError, 'after_action needs a block' if hook.nil?\nafter_action(&hook) # inside the option block","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always write `after_action do |action, args, options| ... end` with a literal block","Check hook variables for nil before passing with `&`","Treat before_action and after_action symmetrically when refactoring"],"tags":["puppet","faces","dsl","block","argumenterror"],"backgroundTag":"missing-block-argument","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}