puppetlabs/puppet · error · ArgumentError
The code loaded from %{source_ref} does not define the plan
Error message
The code loaded from %{source_ref} does not define the plan '%{plan_name}' - no plan found. What it means
Raised by PuppetPlanInstantiator when the plan file contains exactly one definition, but that definition is not a Model::PlanDefinition -- typically a class or function definition sits in the plans/ directory. The loader expects the sole definition in a plan file to be a plan.
Source
Thrown at lib/puppet/pops/loader/puppet_plan_instantiator.rb:45
# perfectly legal and don't count as the file containing multiple
# definitions for this purpose. By this point, we've already validated that
# there are no node definitions *outside* apply blocks, so we simply ignore
# them here.
definitions = result.definitions.reject { |definition| definition.is_a?(Puppet::Pops::Model::NodeDefinition) }
# Only one plan is allowed (and no other definitions)
case definitions.size
when 0
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 asView on GitHub (pinned to e227c27540)
Solutions
- Change the definition keyword to `plan` so the file declares a plan
- Move the file to the directory matching its definition type (manifests/ for classes, functions/ for functions)
- Audit the plans/ directory so it holds only plan files
Example fix
# before - modules/mymod/plans/provision.pp
# (planet typo)planet mymod::provision { include profile::build }
class mymod::provision {
include profile::build
}
# after
plan mymod::provision(
TargetSpec $nodes
) {
run_command('uptime', $nodes)
} Defensive patterns
Strategy: validation
Validate before calling
defs = model.definitions.reject { |d| d.is_a?(Puppet::Pops::Model::NodeDefinition) }
ok = defs.size == 1 && defs[0].is_a?(Puppet::Pops::Model::PlanDefinition)
raise ArgumentError, "#{source_ref} does not contain a plan" unless ok Type guard
defs[0].is_a?(Puppet::Pops::Model::PlanDefinition)
Try / catch
begin
loader.load_typed(typed_name)
rescue ArgumentError => e
raise unless e.message.include?('no plan found')
warn "not a plan definition: #{e.message}"
end Prevention
- Keep plans only in plans/, classes only in manifests/
- When converting a class to a plan, change the keyword first, then move the file
- Lint for plan directories containing non-plan definitions
When it happens
Trigger: `definitions[0]` fails `is_a?(Model::PlanDefinition)`: e.g. plans/provision.pp contains `class mymod::provision { ... }` or a `function` definition instead of `plan mymod::provision { ... }`.
Common situations: Renaming a class manifest into plans/ without changing the `class` keyword to `plan`; writing a function meant for functions/ inside plans/; Bolt project structure mistakes during migration.
Related errors
- The code loaded from %{source_ref} must contain only the pla
- The code loaded from %{source_ref} does not define the plan
- The code loaded from %{source_ref} produced plan with the wr
- The code loaded from %{source} contains additional logic - c
- Module %{module_name} not found in environment %{environment
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/31b9d8053e3e140a.
Report an issue: GitHub.