{"record":{"id":"564b2da979b61cca","repo":"puppetlabs/puppet","slug":"manifest-of-environment-environment-name-app","errorCode":null,"errorMessage":"manifest of environment '#{@environment.name}' appoints directory '#{file}'. It must be a file","messagePattern":"manifest of environment '#(.+?)' appoints directory '#(.+?)'\\. It must be a file","errorType":"exception","errorClass":"Puppet::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/pops/loaders.rb","lineNumber":298,"sourceCode":"  #     environment as a side effect. This is attempted first.\n  #   2. The contents of the environment's +manifest+ attribute: Puppet will\n  #     try to load the environment manifest. The manifest must be a file.\n  #\n  # @return [Model::Program] The manifest parsed into a model object\n  def load_main_manifest\n    parser = Parser::EvaluatingParser.singleton\n    parsed_code = Puppet[:code]\n    program = if parsed_code != \"\"\n                parser.parse_string(parsed_code, 'unknown-source-location')\n              else\n                file = @environment.manifest\n\n                # if the manifest file is a reference to a directory, parse and\n                # combine all .pp files in that directory\n                if file == Puppet::Node::Environment::NO_MANIFEST\n                  nil\n                elsif File.directory?(file)\n                  raise Puppet::Error, \"manifest of environment '#{@environment.name}' appoints directory '#{file}'. It must be a file\"\n                elsif File.exist?(file)\n                  parser.parse_file(file)\n                else\n                  raise Puppet::Error, \"manifest of environment '#{@environment.name}' appoints '#{file}'. It does not exist\"\n                end\n              end\n    instantiate_definitions(program, public_environment_loader) unless program.nil?\n    program\n  rescue Puppet::ParseErrorWithIssue => detail\n    detail.environment = @environment.name\n    raise\n  rescue => detail\n    msg = _('Could not parse for environment %{env}: %{detail}') % { env: @environment, detail: detail }\n    error = Puppet::Error.new(msg)\n    error.set_backtrace(detail.backtrace)\n    raise error\n  end\n","sourceCodeStart":280,"sourceCodeEnd":316,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/pops/loaders.rb#L280-L316","documentation":"When Puppet::Pops::Loaders loads an environment (no Puppet[:code] set), it reads @environment.manifest — the `manifest` setting from environment.conf (or its default). After the NO_MANIFEST escape, it explicitly requires a regular file: File.directory?(file) raises Puppet::Error. Despite the adjacent comment about combining .pp files, this code path (preloading environment definitions into the loaders) only accepts a single manifest file.","triggerScenarios":"environment.conf contains `manifest = manifests` or any path that resolves to a directory, and compilation/lookup triggers Loaders#load_environment; Puppet[:code] is empty so the manifest branch runs.","commonSituations":"Teams setting `manifest` to their manifests/ directory expecting directory-manifest behavior in every code path; site.pp split across files; migrating an environment to environment.conf manifest settings for the first time.","solutions":["Point the manifest setting at one concrete file, e.g. `manifest = manifests/site.pp`.","If you rely on directory manifests for catalog compilation, keep that in the layer that supports it and give the environment a single entry file for this path.","Set `manifest = ` to the no-manifest sentinel behavior only if you truly have no environment manifest (Puppet::Node::Environment::NO_MANIFEST, i.e. the string 'no_manifest')."],"exampleFix":"# before: environments/production/environment.conf\nmanifest = manifests\n\n# after\nmanifest = manifests/site.pp","handlingStrategy":"validation","validationCode":"# Validate environment.conf manifest before deploy\nenv_root = 'environments/production'\nmanifest = nil\nFile.foreach(File.join(env_root, 'environment.conf')) do |line|\n  m = line.match(/^\\s*manifest\\s*=\\s*(\\S+)/) and manifest = m[1]\nend\nif manifest && manifest != 'no_manifest'\n  path = File.expand_path(manifest, env_root)\n  raise \"manifest '#{path}' must be an existing file, not a directory\" unless File.file?(path)\nend","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Set `manifest` in environment.conf to a single .pp file (e.g. manifests/site.pp).","Add a deploy gate that checks File.file?(manifest) for every environment.conf.","Use 'no_manifest' explicitly when an environment intentionally has no manifest."],"tags":["puppet","environment-conf","manifest","environment","puppet-error"],"backgroundTag":"puppet-manifest-must-be-file","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}