puppetlabs/puppet · error · ArgumentError
a data type must have an interface
Error message
a data type must have an interface
What it means
TypeBuilder#create_type requires that the definition block declared a signature via `interface '...'`. If @interface is not a String (the block never called interface, or assigned nil), ArgumentError 'a data type must have an interface' is raised. Every Puppet Ruby data type needs its attributes/functions declared as an interface string so PCore can construct the PObjectType.
Source
Thrown at lib/puppet/datatypes.rb:156
def self.create_loaded_type(type_name, loader, &block)
builder = TypeBuilder.new(type_name.to_s)
api = TypeBuilderAPI.new(builder).freeze
api.instance_eval(&block)
builder.create_type(loader)
end
# @api private
class TypeBuilder
attr_accessor :interface, :implementation, :implementation_class
def initialize(type_name)
@type_name = type_name
@implementation = nil
@implementation_class = nil
end
def create_type(loader)
raise ArgumentError, _('a data type must have an interface') unless @interface.is_a?(String)
created_type = Puppet::Pops::Types::PObjectType.new(
@type_name,
Puppet::Pops::Parser::EvaluatingParser.new.parse_string("{ #{@interface} }").body
)
if !@implementation_class.nil?
if @implementation_class < Puppet::Pops::Types::PuppetObject
@implementation_class.instance_eval do
include Puppet::Pops::Types::PuppetObject
@_pcore_type = created_type
def self._pcore_type
@_pcore_type
end
end
else
Puppet::Pops::Loaders.implementation_registry.register_implementation(created_type, @implementation_class)View on GitHub (pinned to e227c27540)
Solutions
- Add `interface 'attributes => { ... }'` as the first call inside the create_type block
- Model the interface string on Puppet's bundled data types under lib/puppet/datatypes/
- Declare functions in the same interface string if the type exposes behavior: `interface <<-PUPPET` heredoc works for multiline signatures
Example fix
# before
Puppet::DataTypes.create_type('Acme::Thing') do
implementation { def version; '1.0'; end }
end
# after
Puppet::DataTypes.create_type('Acme::Thing') do
interface 'attributes => { name => String[1] }'
implementation { def version; '1.0'; end }
end Defensive patterns
Strategy: validation
Validate before calling
def define_type(name, iface)
raise ArgumentError, "#{name}: interface string required" unless iface.is_a?(String) && !iface.empty?
Puppet::DataTypes.create_type(name) do
interface iface
yield if block_given?
end
end Type guard
def valid_interface?(s)
s.is_a?(String) && begin
Puppet::Pops::Parser::EvaluatingParser.new.parse_string("{ #{s} }")
true
rescue Puppet::ParseError
false
end
end Prevention
- Treat `interface` as the mandatory first line of every create_type block
- Enforce with a spec: load the type and expect no ArgumentError mentioning 'must have an interface'
When it happens
Trigger: Puppet::DataTypes.create_type('Acme::Thing') { implementation { ... } } with no interface call; interface assigned only on a conditional branch that does not run.
Common situations: Converting a plain Ruby class to a Puppet data type and skipping the interface; trimming boilerplate too aggressively when copying from an example type.
Related errors
- Data Type Load Error for type '%{type_name}': %{message}
- a data type can only have one interface
- a data type can only have one implementation
- One or more file(s) specified did not exist: %{files}
- title patterns that use procs are not supported.
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/18fd155b78d6fcb8.
Report an issue: GitHub.