puppetlabs/puppet · error · ArgumentError

Callable tuple may not have a size constraint when used as a

Error message

Callable tuple may not have a size constraint when used as args

What it means

PTupleType#callable_args? answers whether this tuple can be spread as the argument list of a Callable type. A tuple carrying a size_type (Tuple[String, Integer, 1, 3]) denotes a variable number of elements, which cannot be matched element-wise against a signature, so the method raises ArgumentError instead of guessing. It is a private API of the pops type checker; users normally hit it only indirectly, when a Callable is combined with sized tuple/array types in aliases or in Ruby-side signature checks.

Source

Thrown at lib/puppet/pops/types/types.rb:2181

  end

  # If set, describes min and max required of the given types - if max > size of
  # types, the last type entry repeats
  #
  attr_reader :size_type

  attr_reader :types

  def accept(visitor, guard)
    super
    @size_type.accept(visitor, guard) unless @size_type.nil?
    @types.each { |elem| elem.accept(visitor, guard) }
  end

  # @api private
  def callable_args?(callable_t, guard)
    unless size_type.nil?
      raise ArgumentError, 'Callable tuple may not have a size constraint when used as args'
    end

    params_tuple = callable_t.param_types
    param_block_t = callable_t.block_type
    arg_types = @types
    arg_block_t = arg_types.last
    if arg_block_t.kind_of_callable?(true, guard)
      # Can't pass a block to a callable that doesn't accept one
      return false if param_block_t.nil?

      # Check that the block is of the right tyṕe
      return false unless param_block_t.assignable?(arg_block_t, guard)

      # Check other arguments
      arg_count = arg_types.size - 1
      params_size_t = params_tuple.size_type || PIntegerType.new(*params_tuple.size_range)
      return false unless params_size_t.assignable?(PIntegerType.new(arg_count, arg_count), guard)

View on GitHub (pinned to e227c27540)

Solutions

  1. Remove the size constraint from the tuple used on the argument side: Tuple[String, Integer] instead of Tuple[String, Integer, 1, 3].
  2. Declare the callable's parameters explicitly (Callable[String, Integer, 2]) instead of one sized tuple type.
  3. In Ruby, rebuild the tuple without its size type before checking: PTupleType.new(tuple.types).
  4. If it reproduces from plain Puppet DSL with no Ruby API use, minimize the case and report it upstream as a type-system bug.

Example fix

# before (type alias)
type MyCallback = Callable[Tuple[String, 1, 2]]   # sized tuple as args -> ArgumentError when checked

# after
type MyCallback = Callable[String, String]        # declare the parameters explicitly
Defensive patterns

Strategy: validation

Validate before calling

# Ruby: reject sized tuples before the call
if tuple.respond_to?(:size_type) && !tuple.size_type.nil?
  raise ArgumentError, 'tuple has a size constraint; strip it before callable_args?'
end
tuple.callable_args?(callable, guard)

Type guard

def fixed_size_tuple?(t)
  t.is_a?(Puppet::Pops::Types::PTupleType) && t.size_type.nil?
end

Try / catch

begin
  tuple.callable_args?(callable, guard)
rescue ArgumentError => e
  raise unless e.message =~ /size constraint/
  PTupleType.new(tuple.types).callable_args?(callable, guard)  # retry without the size type
end

Prevention

When it happens

Trigger: Calling tuple.callable_args?(callable, guard) in Ruby on a tuple built with a size type, e.g. PTupleType.new(types, PIntegerType.new(1, 3)); type aliases that put a sized Tuple or an Array type on the argument side of a Callable, then trigger an assignability/callability check during compilation.

Common situations: Advanced callback/dispatch type aliases (Callable[Array[String, 1]]); Ruby tools embedding pops (custom functions, linters) doing signature checks; Puppet upgrades that change when the checker invokes callable_args?.

Related errors


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