puppetlabs/puppet · error · ArgumentError

Attempt to redefine block

Error message

Attempt to redefine block

What it means

A dispatch signature may declare at most one block parameter. The first block_param (or optional_block_param, which delegates to it) sets @block_type; any second call finds @block_type non-nil and raises this ArgumentError.

Source

Thrown at lib/puppet/functions.rb:503

        type, name = type_and_name
        type = Puppet::Pops::Types::TypeParser.singleton.parse(type, loader) unless type.is_a?(Puppet::Pops::Types::PAnyType)
      else
        raise ArgumentError, _("block_param accepts max 2 arguments (type, name), got %{size}.") % { size: type_and_name.size }
      end

      unless Puppet::Pops::Types::TypeCalculator.is_kind_of_callable?(type, false)
        raise ArgumentError, _("Expected PCallableType or PVariantType thereof, got %{type_class}") % { type_class: type.class }
      end

      unless name.is_a?(Symbol)
        raise ArgumentError, _("Expected block_param name to be a Symbol, got %{name_class}") % { name_class: name.class }
      end

      if @block_type.nil?
        @block_type = type
        @block_name = name
      else
        raise ArgumentError, _('Attempt to redefine block')
      end
    end
    alias required_block_param block_param

    # Defines one optional block parameter that may appear last. If type or name is missing the
    # defaults are "any callable", and the name is "block". The implementor of the dispatch target
    # must use block = nil when it is optional (or an error is raised when the call is made).
    #
    # @api public
    def optional_block_param(*type_and_name)
      # same as required, only wrap the result in an optional type
      required_block_param(*type_and_name)
      @block_type = Puppet::Pops::Types::TypeFactory.optional(@block_type)
    end

    # Defines the return type. Defaults to 'Any'
    # @param [String] type a reference to a Puppet Data Type
    #

View on GitHub (pinned to e227c27540)

Solutions

  1. Keep exactly one block_param or optional_block_param per dispatch block.
  2. If two callbacks are needed, pass the second as a regular param typed 'Callable[...]' and call it with the call() function or a lambda.
  3. Split the signature into two dispatch blocks if the two blocks belong to different call shapes.

Example fix

# before: two block parameters in one dispatch
 dispatch :m do
   param 'String', :s
   block_param 'Callable', :block
   optional_block_param 'Callable', :on_error   # raises: redefine
 end

# after: one block, second callback is a Callable param
 dispatch :m do
   param 'String', :s
   param 'Callable', :on_error
   block_param 'Callable', :block
 end
Defensive patterns

Strategy: validation

Validate before calling

# CI grep: more than one block-param declaration inside a single dispatch block
! grep -Pzo 'dispatch[^}]*block_param[^}]*\n[^}]*(block_param|optional_block_param)' lib/puppet/functions/**/*.rb

Prevention

When it happens

Trigger: block_param 'Callable', :block followed later in the same dispatch block by optional_block_param 'Callable', :fallback, or by a second block_param with a different name.

Common situations: Trying to accept two different blocks (a transformer and an error handler) in one signature; copy-paste of an existing block_param line when adding a new parameter.

Related errors


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