puppetlabs/puppet · error · ArgumentError

The code loaded from

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.

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.

Appendix: 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)