puppetlabs/puppet · error · Puppet::DataBinding::LookupError

%{path}: file does not contain a valid yaml hash

Error message

%{path}: file does not contain a valid yaml hash

What it means

Puppet's built-in Hiera 'yaml_data' data_hash function loads each YAML file with Puppet::Util::Yaml.safe_load and expects a top-level mapping. If the document is anything other than a Hash (a sequence, a scalar, or a comments-only file that parses as nil), the data cannot serve key lookups: under strict=error Puppet raises Puppet::DataBinding::LookupError, otherwise it logs a warning and treats the file as empty. A literal 'false' document is exempted from the raise because empty YAML files can historically parse as false.

Source

Thrown at lib/puppet/functions/yaml_data.rb:31

  dispatch :yaml_data do
    param 'Struct[{path=>String[1]}]', :options
    param 'Puppet::LookupContext', :context
  end

  argument_mismatch :missing_path do
    param 'Hash', :options
    param 'Puppet::LookupContext', :context
  end

  def yaml_data(options, context)
    path = options['path']
    context.cached_file_data(path) do |content|
      data = Puppet::Util::Yaml.safe_load(content, [Symbol], path)
      if data.is_a?(Hash)
        Puppet::Pops::Lookup::HieraConfig.symkeys_to_string(data)
      else
        msg = _("%{path}: file does not contain a valid yaml hash" % { path: path })
        raise Puppet::DataBinding::LookupError, msg if Puppet[:strict] == :error && data != false

        Puppet.warning(msg)
        {}
      end
    rescue Puppet::Util::Yaml::YamlLoadError => ex
      # YamlLoadErrors include the absolute path to the file, so no need to add that
      raise Puppet::DataBinding::LookupError, _("Unable to parse %{message}") % { message: ex.message }
    end
  end

  def missing_path(options, context)
    "one of 'path', 'paths' 'glob', 'globs' or 'mapped_paths' must be declared in hiera.yaml when using this data_hash function"
  end
end

View on GitHub (pinned to e227c27540)

Solutions

  1. Reformat the file named in the message so the top level is a mapping (keys at column 0, list items indented under a key).
  2. Run 'puppet lookup <key> --explain' to see exactly which path failed.
  3. Sanity-check with: ruby -ryaml -e "p YAML.safe_load(File.read('file.yaml')).is_a?(Hash)".
  4. Tighten path/glob patterns so non-data YAML files are never matched.

Example fix

# datadir/common.yaml - before (top-level list)
- ntp::servers: ['pool.ntp.org']
- profile::timezone: 'UTC'

# after (top-level mapping)
ntp::servers:
  - 'pool.ntp.org'
profile::timezone: 'UTC'
Defensive patterns

Strategy: validation

Validate before calling

require 'yaml'

def valid_hiera_data?(path)
  doc = YAML.safe_load(File.read(path), permitted_classes: [], aliases: false)
  doc.is_a?(Hash)
rescue Psych::SyntaxError, StandardError
  false
end

bad = Dir['data/**/*.yaml'].reject { |f| valid_hiera_data?(f) }
abort "top-level node is not a mapping: #{bad.join(', ')}" unless bad.empty?

Try / catch

begin
  Puppet::LookupContext.new.lookup('mykey')
rescue Puppet::DataBinding::LookupError => e
  # message contains the offending path; fall back to a default value
  Puppet.err "hiera data invalid: #{e.message}"
  default_value
end

Prevention

When it happens

Trigger: hiera.yaml declares data_hash: yaml_data with path/paths/glob/mapped_paths selecting a file whose top-level node is a YAML list ('- key: value' at column 0), a bare scalar, or an empty/comments-only file; or a broad glob (e.g. 'data/*.yaml') matches a non-Hiera YAML file. The raise additionally requires Puppet[:strict] == :error and the parsed value != false.

Common situations: Writing hiera data as a top-level list instead of nesting it under a key; a truncated or half-deployed file; a glob picking up unrelated YAML (k8s manifests, docker-compose); an empty file created by a failed template render; CI passing locally but failing on a strict-mode master.

Related errors


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