puppetlabs/puppet · error · ArgumentError
The code loaded from %{source} contains additional logic - c
Error message
The code loaded from %{source} contains additional logic - can only contain the function %{name} What it means
Raised by PuppetFunctionInstantiator when a Puppet-language function file parses, defines the correctly-named function, but the program body is not exactly that single definition -- the file contains additional top-level logic outside the function. A module function file (functions/*.pp) must contain the function definition and nothing else. It is an ArgumentError thrown at loader-instantiation time, so compilation or plan resolution aborts when the function is first requested.
Source
Thrown at lib/puppet/pops/loader/puppet_function_instantiator.rb:45
raise ArgumentError, _("The code loaded from %{source_ref} does not define the function '%{func_name}' - it is empty.") % { source_ref: source_ref, func_name: typed_name.name }
when 1
# ok
else
raise ArgumentError, _("The code loaded from %{source_ref} must contain only the function '%{type_name}' - it has additional definitions.") % { source_ref: source_ref, type_name: typed_name.name }
end
the_function_definition = result.definitions[0]
unless the_function_definition.is_a?(Model::FunctionDefinition)
raise ArgumentError, _("The code loaded from %{source_ref} does not define the function '%{type_name}' - no function found.") % { source_ref: source_ref, type_name: typed_name.name }
end
unless the_function_definition.name == typed_name.name
expected = typed_name.name
actual = the_function_definition.name
raise ArgumentError, _("The code loaded from %{source_ref} produced function with the wrong name, expected %{expected}, actual %{actual}") % { source_ref: source_ref, expected: expected, actual: actual }
end
unless result.body == the_function_definition
raise ArgumentError, _("The code loaded from %{source} contains additional logic - can only contain the function %{name}") % { source: source_ref, name: typed_name.name }
end
# Adapt the function definition with loader - this is used from logic contained in it body to find the
# loader to use when making calls to the new function API. Such logic have a hard time finding the closure (where
# the loader is known - hence this mechanism
private_loader = loader.private_loader
Adapters::LoaderAdapter.adapt(the_function_definition).loader_name = private_loader.loader_name
# Cannot bind loaded functions to global scope, that must be done without binding that scope as
# loaders survive a compilation.
closure_scope = nil # Puppet.lookup(:global_scope) { {} }
created = create_function_class(the_function_definition)
# 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
created.new(closure_scope, private_loader)View on GitHub (pinned to e227c27540)
Solutions
- Delete every top-level statement so the file contains only the function definition
- Move logic into the function body or into a separate private function in the same module
- Use the source_ref named in the message to locate the exact file and remove the extra logic it reports
Example fix
# before - modules/mymod/functions/double.pp
notice('loading double')
$factor = 2
function mymod::double(Integer $x) { $x * $factor }
# after - only the definition remains
function mymod::double(Integer $x) { $x * 2 } Defensive patterns
Strategy: validation
Validate before calling
# Pre-validate a function file the way the instantiator does
parser = Puppet::Pops::Parser::Parser.new
model = parser.parse_string(File.read(path), path)
defs = model.definitions
valid = defs.size == 1 &&
defs[0].is_a?(Puppet::Pops::Model::FunctionDefinition) &&
model.body == defs[0] &&
defs[0].name == expected_name
raise ArgumentError, "#{path} must contain only function #{expected_name}" unless valid Try / catch
begin
fun = loader.load_typed(typed_name)
rescue ArgumentError => e
raise unless e.message.include?('contains additional logic')
warn "skipping #{typed_name.name}: #{e.message}"
end Prevention
- Keep exactly one function definition per file under functions/ with no other statements
- Run puppet parser validate or puppet-lint on function files before committing
- Never reuse manifest-style top-level code inside function files
When it happens
Trigger: The check `result.body == the_function_definition` fails after the earlier name/type checks passed: e.g. modules/mymod/functions/double.pp has a notice() call, a file-scoped $variable, or any other expression before/after `function mymod::double(Integer $x) { ... }`.
Common situations: Adding debug notice() or logging at file top level; pasting usage examples next to the function; generators/templates emitting code around the definition; refactoring a class manifest into functions/ while keeping class-style top-level code.
Related errors
- The code loaded from %{source_ref} does not define the plan
- The code loaded from %{source} contains additional logic - c
- The code loaded from %{source_ref} does not create the resou
- The code loaded from %{source_ref} does not create the resou
- The code loaded from %{source_ref} must contain only the cre
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/ede0cbc6744ef308.
Report an issue: GitHub.