{"record":{"id":"7584c3d91943d340","repo":"puppetlabs/puppet","slug":"the-code-loaded-from-source-ref-does-not-seem-t-7584c3","errorCode":null,"errorMessage":"The code loaded from %{source_ref} does not seem to be a Puppet 4x API function - no create_function call.","messagePattern":"The code loaded from %(.+?) does not seem to be a Puppet 4x API function - no create_function call\\.","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/loader/ruby_function_instantiator.rb","lineNumber":19,"sourceCode":"# frozen_string_literal: true\n\n# The RubyFunctionInstantiator instantiates a Puppet::Functions::Function given the ruby source\n# that calls Puppet::Functions.create_function.\n#\nclass Puppet::Pops::Loader::RubyFunctionInstantiator\n  # Produces an instance of the Function class with the given typed_name, or fails with an error if the\n  # given ruby source does not produce this instance when evaluated.\n  #\n  # @param loader [Puppet::Pops::Loader::Loader] The loader the function is associated with\n  # @param typed_name [Puppet::Pops::Loader::TypedName] the type / name of the function to load\n  # @param source_ref [URI, String] a reference to the source / origin of the ruby code to evaluate\n  # @param ruby_code_string [String] ruby code in a string\n  #\n  # @return [Puppet::Pops::Functions.Function] - an instantiated function with global scope closure associated with the given loader\n  #\n  def self.create(loader, typed_name, source_ref, ruby_code_string)\n    unless ruby_code_string.is_a?(String) && ruby_code_string =~ /Puppet::Functions\\.create_function/\n      raise ArgumentError, _(\"The code loaded from %{source_ref} does not seem to be a Puppet 4x API function - no create_function call.\") % { source_ref: source_ref }\n    end\n\n    # make the private loader available in a binding to allow it to be passed on\n    loader_for_function = loader.private_loader\n    here = get_binding(loader_for_function)\n    created = eval(ruby_code_string, here, source_ref, 1) # rubocop:disable Security/Eval\n    unless created.is_a?(Class)\n      raise ArgumentError, _(\"The code loaded from %{source_ref} did not produce a Function class when evaluated. Got '%{klass}'\") % { source_ref: source_ref, klass: created.class }\n    end\n    unless created.name.to_s == typed_name.name()\n      raise ArgumentError, _(\"The code loaded from %{source_ref} produced mis-matched name, expected '%{type_name}', got %{created_name}\") % { source_ref: source_ref, type_name: typed_name.name, created_name: created.name }\n    end\n\n    # create the function instance - it needs closure (scope), and loader (i.e. where it should start searching for things\n    # when calling functions etc.\n    # It should be bound to global scope\n\n    # Sets closure scope to nil, to let it be picked up at runtime from Puppet.lookup(:global_scope)","sourceCodeStart":1,"sourceCodeEnd":37,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/loader/ruby_function_instantiator.rb#L1-L37","documentation":"Raised by RubyFunctionInstantiator when Ruby source loaded as a 4x API function does not contain a Puppet::Functions.create_function call (regex gate before eval). Modern Ruby functions live in lib/puppet/functions/ (namespaced) and must be defined via create_function; anything else fails fast.","triggerScenarios":"`ruby_code_string !~ /Puppet::Functions\\.create_function/`: a .rb file resolved as a 4x function lacks the call -- e.g. legacy 3x `newfunction` code left where the 4x loader finds it, or plain Ruby helpers in the functions path.","commonSituations":"Migrating 3x functions while files remain in or resolve through 4x paths; writing plain Ruby utility classes in lib/puppet/functions/; broken packaging that ships the wrong file under the function name.","solutions":["Rewrite the function using Puppet::Functions.create_function(:'mod::name') do ... end","Keep legacy 3x functions under lib/puppet/parser/functions/<name>.rb so the legacy instantiator handles them","Verify the source_ref to confirm which file the 4x loader is opening"],"exampleFix":"# before - lib/puppet/functions/mymod/double.rb\nmodule Puppet::Parser::Functions\n  newfunction(:double, :type => :rvalue) { |args| args[0] * 2 }\nend\n\n# after\nPuppet::Functions.create_function(:'mymod::double') do\n  dispatch :double do\n    param 'Integer', :x\n  end\n  def double(x); x * 2; end\nend","handlingStrategy":"validation","validationCode":"raise ArgumentError, \"#{path} is not a Puppet 4x function\" unless ruby_source.is_a?(String) && ruby_source =~ /Puppet::Functions\\.create_function/","typeGuard":null,"tryCatchPattern":"begin\n  fn = loader.load_typed(typed_name)\nrescue ArgumentError => e\n  raise unless e.message.include?('no create_function call')\n  warn \"not a 4x Ruby function: #{e.message}\"\nend","preventionTips":["Put 4x functions only in lib/puppet/functions/ with module namespacing","Put 3x functions only in lib/puppet/parser/functions/","Automate 3x-to-4x migration with a conversion tool, then grep directories for the wrong API"],"tags":["puppet","function","ruby","loader","eval"],"backgroundTag":"puppet-function-load-validation","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}