puppetlabs/puppet · error · ArgumentError
The code loaded from %{source_ref} did not produce a Functio
Error message
The code loaded from %{source_ref} did not produce a Function class when evaluated. Got '%{klass}' What it means
Raised by RubyFunctionInstantiator when the Ruby source evals successfully but does not produce a Class. Puppet::Functions.create_function returns the generated anonymous function class; if the file's value is anything else (nil, String, object) this ArgumentError reports the actual class.
Source
Thrown at lib/puppet/pops/loader/ruby_function_instantiator.rb:27
#
# @param loader [Puppet::Pops::Loader::Loader] The loader the function is associated with
# @param typed_name [Puppet::Pops::Loader::TypedName] the type / name of the function to load
# @param source_ref [URI, String] a reference to the source / origin of the ruby code to evaluate
# @param ruby_code_string [String] ruby code in a string
#
# @return [Puppet::Pops::Functions.Function] - an instantiated function with global scope closure associated with the given loader
#
def self.create(loader, typed_name, source_ref, ruby_code_string)
unless ruby_code_string.is_a?(String) && ruby_code_string =~ /Puppet::Functions\.create_function/
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 }
end
# make the private loader available in a binding to allow it to be passed on
loader_for_function = loader.private_loader
here = get_binding(loader_for_function)
created = eval(ruby_code_string, here, source_ref, 1) # rubocop:disable Security/Eval
unless created.is_a?(Class)
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 }
end
unless created.name.to_s == typed_name.name()
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 }
end
# create the function instance - it needs closure (scope), and loader (i.e. where it should start searching for things
# when calling functions etc.
# It should be bound to global scope
# Sets closure scope to nil, to let it be picked up at runtime from Puppet.lookup(:global_scope)
# If function definition used the loader from the binding to create a new loader, that loader wins
created.new(nil, loader_for_function)
end
# Produces a binding where the given loader is bound as a local variable (loader_injected_arg). This variable can be used in loaded
# ruby code - e.g. to call Puppet::Function.create_loaded_function(:name, loader,...)
#
def self.get_binding(loader_injected_arg)View on GitHub (pinned to e227c27540)
Solutions
- Make Puppet::Functions.create_function(...) the final expression of the file
- Use the 'Got' class from the message to find the trailing expression and move it above the definition
Example fix
# before
Puppet::Functions.create_function(:'mymod::double') do ... end
Puppet.info('loaded double')
# after
Puppet.info('loading double')
Puppet::Functions.create_function(:'mymod::double') do ... end Defensive patterns
Strategy: try-catch
Type guard
def function_class?(obj) obj.is_a?(Class) && obj < Puppet::Pops::Functions::Function end
Try / catch
begin
fn = loader.load_typed(typed_name)
rescue ArgumentError => e
raise unless e.message.include?('did not produce a Function class')
warn "file does not evaluate to a function class: #{e.message}"
end Prevention
- Make create_function the last expression of every function file
- Add statements above, never below, the definition
- Smoke-load all functions in CI (call Puppet::Pal or the loader once per function)
When it happens
Trigger: `eval(...)` result fails `is_a?(Class)`: the last expression of the file is not the create_function call -- e.g. trailing statements after the definition block, or the call wrapped in a conditional leaving another expression as the file value.
Common situations: Appending logging/configuration after the create_function block; refactorings that change the file's final expression; editing generated functions and adding code at the bottom.
Related errors
- The code loaded from %{source_ref} does not seem to be a Pup
- The code loaded from %{source_ref} does not seem to be a Pup
- The code loaded from %{source_ref} did not produce a data ty
- The code loaded from %{source_ref} produced mis-matched name
- The code loaded from %{source_ref} does not seem to be a Pup
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/f3f558c287d0e957.
Report an issue: GitHub.