{"record":{"id":"ede0cbc6744ef308","repo":"puppetlabs/puppet","slug":"the-code-loaded-from-source-contains-additional","errorCode":null,"errorMessage":"The code loaded from %{source} contains additional logic - can only contain the function %{name}","messagePattern":"The code loaded from %(.+?) contains additional logic - can only contain the function %(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/loader/puppet_function_instantiator.rb","lineNumber":45,"sourceCode":"      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 }\n    when 1\n      # ok\n    else\n      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 }\n    end\n    the_function_definition = result.definitions[0]\n\n    unless the_function_definition.is_a?(Model::FunctionDefinition)\n      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 }\n    end\n\n    unless the_function_definition.name == typed_name.name\n      expected = typed_name.name\n      actual = the_function_definition.name\n      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 }\n    end\n    unless result.body == the_function_definition\n      raise ArgumentError, _(\"The code loaded from %{source} contains additional logic - can only contain the function %{name}\") % { source: source_ref, name: typed_name.name }\n    end\n\n    # Adapt the function definition with loader - this is used from logic contained in it body to find the\n    # loader to use when making calls to the new function API. Such logic have a hard time finding the closure (where\n    # the loader is known - hence this mechanism\n    private_loader = loader.private_loader\n    Adapters::LoaderAdapter.adapt(the_function_definition).loader_name = private_loader.loader_name\n\n    # Cannot bind loaded functions to global scope, that must be done without binding that scope as\n    # loaders survive a compilation.\n    closure_scope = nil # Puppet.lookup(:global_scope) { {} }\n\n    created = create_function_class(the_function_definition)\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    created.new(closure_scope, private_loader)","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/loader/puppet_function_instantiator.rb#L27-L63","documentation":"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.","triggerScenarios":"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) { ... }`.","commonSituations":"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.","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"],"exampleFix":"# before - modules/mymod/functions/double.pp\nnotice('loading double')\n$factor = 2\nfunction mymod::double(Integer $x) { $x * $factor }\n\n# after - only the definition remains\nfunction mymod::double(Integer $x) { $x * 2 }","handlingStrategy":"validation","validationCode":"# Pre-validate a function file the way the instantiator does\nparser = Puppet::Pops::Parser::Parser.new\nmodel  = parser.parse_string(File.read(path), path)\ndefs   = model.definitions\nvalid  = defs.size == 1 &&\n         defs[0].is_a?(Puppet::Pops::Model::FunctionDefinition) &&\n         model.body == defs[0] &&\n         defs[0].name == expected_name\nraise ArgumentError, \"#{path} must contain only function #{expected_name}\" unless valid","typeGuard":null,"tryCatchPattern":"begin\n  fun = loader.load_typed(typed_name)\nrescue ArgumentError => e\n  raise unless e.message.include?('contains additional logic')\n  warn \"skipping #{typed_name.name}: #{e.message}\"\nend","preventionTips":["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"],"tags":["puppet","puppet-language","loader","function","module"],"backgroundTag":"puppet-function-load-validation","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}