{"record":{"id":"18fd155b78d6fcb8","repo":"puppetlabs/puppet","slug":"a-data-type-must-have-an-interface","errorCode":null,"errorMessage":"a data type must have an interface","messagePattern":"a data type must have an interface","errorType":"validation","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/datatypes.rb","lineNumber":156,"sourceCode":"  def self.create_loaded_type(type_name, loader, &block)\n    builder = TypeBuilder.new(type_name.to_s)\n    api = TypeBuilderAPI.new(builder).freeze\n    api.instance_eval(&block)\n    builder.create_type(loader)\n  end\n\n  # @api private\n  class TypeBuilder\n    attr_accessor :interface, :implementation, :implementation_class\n\n    def initialize(type_name)\n      @type_name = type_name\n      @implementation = nil\n      @implementation_class = nil\n    end\n\n    def create_type(loader)\n      raise ArgumentError, _('a data type must have an interface') unless @interface.is_a?(String)\n\n      created_type = Puppet::Pops::Types::PObjectType.new(\n        @type_name,\n        Puppet::Pops::Parser::EvaluatingParser.new.parse_string(\"{ #{@interface} }\").body\n      )\n\n      if !@implementation_class.nil?\n        if @implementation_class < Puppet::Pops::Types::PuppetObject\n          @implementation_class.instance_eval do\n            include Puppet::Pops::Types::PuppetObject\n            @_pcore_type = created_type\n\n            def self._pcore_type\n              @_pcore_type\n            end\n          end\n        else\n          Puppet::Pops::Loaders.implementation_registry.register_implementation(created_type, @implementation_class)","sourceCodeStart":138,"sourceCodeEnd":174,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/datatypes.rb#L138-L174","documentation":"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.","triggerScenarios":"Puppet::DataTypes.create_type('Acme::Thing') { implementation { ... } } with no interface call; interface assigned only on a conditional branch that does not run.","commonSituations":"Converting a plain Ruby class to a Puppet data type and skipping the interface; trimming boilerplate too aggressively when copying from an example type.","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"],"exampleFix":"# before\nPuppet::DataTypes.create_type('Acme::Thing') do\n  implementation { def version; '1.0'; end }\nend\n# after\nPuppet::DataTypes.create_type('Acme::Thing') do\n  interface 'attributes => { name => String[1] }'\n  implementation { def version; '1.0'; end }\nend","handlingStrategy":"validation","validationCode":"def define_type(name, iface)\n  raise ArgumentError, \"#{name}: interface string required\" unless iface.is_a?(String) && !iface.empty?\n  Puppet::DataTypes.create_type(name) do\n    interface iface\n    yield if block_given?\n  end\nend","typeGuard":"def valid_interface?(s)\n  s.is_a?(String) && begin\n    Puppet::Pops::Parser::EvaluatingParser.new.parse_string(\"{ #{s} }\")\n    true\n  rescue Puppet::ParseError\n    false\n  end\nend","tryCatchPattern":null,"preventionTips":["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'"],"tags":["puppet","data-types","pcore","interface","validation"],"backgroundTag":"incomplete-type-definition","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}