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 plan %{plan_name}

What it means

Raised by PuppetPlanInstantiator when the plan file's definition checks pass but the program body is not exactly the plan definition -- there is top-level logic outside the plan statement. A plan file may contain only the plan; any expression before or after it triggers this ArgumentError.

Source

Thrown at lib/puppet/pops/loader/puppet_plan_instantiator.rb:54

      raise ArgumentError, _("The code loaded from %{source_ref} does not define the plan '%{plan_name}' - it is empty.") % { source_ref: source_ref, plan_name: typed_name.name }
    when 1
      # ok
    else
      raise ArgumentError, _("The code loaded from %{source_ref} must contain only the plan '%{plan_name}' - it has additional definitions.") % { source_ref: source_ref, plan_name: typed_name.name }
    end
    the_plan_definition = definitions[0]

    unless the_plan_definition.is_a?(Model::PlanDefinition)
      raise ArgumentError, _("The code loaded from %{source_ref} does not define the plan '%{plan_name}' - no plan found.") % { source_ref: source_ref, plan_name: typed_name.name }
    end

    unless the_plan_definition.name == typed_name.name
      expected = typed_name.name
      actual = the_plan_definition.name
      raise ArgumentError, _("The code loaded from %{source_ref} produced plan with the wrong name, expected %{expected}, actual %{actual}") % { source_ref: source_ref, expected: expected, actual: actual }
    end
    unless result.body == the_plan_definition
      raise ArgumentError, _("The code loaded from %{source} contains additional logic - can only contain the plan %{plan_name}") % { source: source_ref, plan_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_plan_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

    created = create_function_class(the_plan_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

  1. Remove all statements outside the plan body
  2. Move initialization logic inside the plan (first steps or parameter defaults)
  3. Re-check the file named by source_ref for anything other than the single plan statement

Example fix

# before - modules/mymod/plans/deploy.pp
$msg = 'deploying'
notice($msg)
plan mymod::deploy($n) { run_command('uptime', $n) }

# after
plan mymod::deploy($n) {
  notice('deploying')
  run_command('uptime', $n)
}
Defensive patterns

Strategy: validation

Validate before calling

ok = model.body == defs[0]
raise ArgumentError, "#{source_ref} contains logic outside the plan" unless ok

Try / catch

begin
  loader.load_typed(typed_name)
rescue ArgumentError => e
  raise unless e.message.include?('can only contain the plan')
  warn "stray top-level logic: #{e.message}"
end

Prevention

When it happens

Trigger: `result.body != the_plan_definition`: e.g. plans/deploy.pp starts with `$msg = 'deploying'` or `notice($msg)` at file scope above/below the `plan` statement.

Common situations: Adding debug output at the top of a plan file; leaving stray expressions after refactoring; generators emitting boilerplate around the plan.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/7af01bf3b6b91514. Report an issue: GitHub.