{"record":{"id":"31b9d8053e3e140a","repo":"puppetlabs/puppet","slug":"the-code-loaded-from-source-ref-does-not-define-31b9d8","errorCode":null,"errorMessage":"The code loaded from %{source_ref} does not define the plan '%{plan_name}' - no plan found.","messagePattern":"The code loaded from %(.+?) does not define the plan '%(.+?)' - no plan found\\.","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/loader/puppet_plan_instantiator.rb","lineNumber":45,"sourceCode":"    # perfectly legal and don't count as the file containing multiple\n    # definitions for this purpose. By this point, we've already validated that\n    # there are no node definitions *outside* apply blocks, so we simply ignore\n    # them here.\n    definitions = result.definitions.reject { |definition| definition.is_a?(Puppet::Pops::Model::NodeDefinition) }\n\n    # Only one plan is allowed (and no other definitions)\n    case definitions.size\n    when 0\n      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 }\n    when 1\n      # ok\n    else\n      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 }\n    end\n    the_plan_definition = definitions[0]\n\n    unless the_plan_definition.is_a?(Model::PlanDefinition)\n      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 }\n    end\n\n    unless the_plan_definition.name == typed_name.name\n      expected = typed_name.name\n      actual = the_plan_definition.name\n      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 }\n    end\n    unless result.body == the_plan_definition\n      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 }\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_plan_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","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/loader/puppet_plan_instantiator.rb#L27-L63","documentation":"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.","triggerScenarios":"`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 { ... }`.","commonSituations":"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.","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"],"exampleFix":"# before - modules/mymod/plans/provision.pp\n# (planet typo)planet mymod::provision { include profile::build }\nclass mymod::provision {\n  include profile::build\n}\n\n# after\nplan mymod::provision(\n  TargetSpec $nodes\n) {\n  run_command('uptime', $nodes)\n}","handlingStrategy":"validation","validationCode":"defs = model.definitions.reject { |d| d.is_a?(Puppet::Pops::Model::NodeDefinition) }\nok = defs.size == 1 && defs[0].is_a?(Puppet::Pops::Model::PlanDefinition)\nraise ArgumentError, \"#{source_ref} does not contain a plan\" unless ok","typeGuard":"defs[0].is_a?(Puppet::Pops::Model::PlanDefinition)","tryCatchPattern":"begin\n  loader.load_typed(typed_name)\nrescue ArgumentError => e\n  raise unless e.message.include?('no plan found')\n  warn \"not a plan definition: #{e.message}\"\nend","preventionTips":["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"],"tags":["puppet","bolt","plan","loader","module-structure"],"backgroundTag":"puppet-plan-load-validation","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}