puppetlabs/puppet · error · Puppet::DataBinding::LookupError
value of %{opts} must be a hash
Error message
value of %{opts} must be a hash What it means
LookupAdapter#validate_lookup_options is the entry gate for the special 'lookup_options' key found in data: before any pattern or key handling, it requires the value to be a Hash, otherwise Puppet::DataBinding::LookupError 'value of lookup_options must be a hash' is raised. lookup_options must always be a mapping of data keys (or '^' regex patterns) to option hashes like {'convert_to' => ..., 'merge' => ...}.
Source
Thrown at lib/puppet/pops/lookup/lookup_adapter.rb:300
def global_only?
instance_variable_defined?(:@global_only) ? @global_only : false
end
# Instructs the lookup framework to only perform lookups in the global layer
# @return [LookupAdapter] self
def set_global_only
@global_only = true
self
end
private
PROVIDER_STACK = [:lookup_global, :lookup_in_environment, :lookup_in_module].freeze
def validate_lookup_options(options, module_name)
return nil if options.nil?
raise Puppet::DataBinding::LookupError, _("value of %{opts} must be a hash") % { opts: LOOKUP_OPTIONS } unless options.is_a?(Hash)
return options if module_name.nil?
pfx = "#{module_name}::"
options.each_pair do |key, _value|
if key.start_with?(LOOKUP_OPTIONS_PATTERN_START)
unless key[1..pfx.length] == pfx
raise Puppet::DataBinding::LookupError, _("all %{opts} patterns must match a key starting with module name '%{module_name}'") % { opts: LOOKUP_OPTIONS, module_name: module_name }
end
else
unless key.start_with?(pfx)
raise Puppet::DataBinding::LookupError, _("all %{opts} keys must start with module name '%{module_name}'") % { opts: LOOKUP_OPTIONS, module_name: module_name }
end
end
end
end
def compile_patterns(options)
return nil if options.nil?View on GitHub (pinned to e227c27540)
Solutions
- Make lookup_options a mapping: `lookup_options: { 'mykey': { 'merge' => 'deep' } }`.
- If you do not want options, delete the lookup_options entry entirely rather than setting a non-hash value.
- Run the data file through a YAML linter — indentation mistakes are the usual root cause.
Example fix
# before: data/common.yaml
lookup_options: 'enabled'
# after
lookup_options:
myapp::config:
merge: deep Defensive patterns
Strategy: validation
Validate before calling
# CI check: any lookup_options key in data must map to a Hash
require 'yaml'
Dir['**/data/**/*.yaml'].each do |f|
lo = YAML.load_file(f)['lookup_options']
next if lo.nil?
abort "#{f}: lookup_options must be a Hash, got #{lo.class}" unless lo.is_a?(Hash)
end Prevention
- lookup_options is always a map of key/pattern → options hash; never a scalar or list.
- Lint YAML data files (yamllint) so indentation produces the structure you intended.
- To disable options for a key, delete its entry — do not change lookup_options' shape.
When it happens
Trigger: A data file containing `lookup_options: true`, `lookup_options: [a, b]`, or `lookup_options: 'auto'` (string); YAML indentation error making the value a scalar; using lookup_options as a flag instead of a map.
Common situations: Teams toggling lookup_options on/off as a boolean; malformed YAML where nested options de-indented into a scalar; copy-paste from notes that drop the mapping structure.
Related errors
- %{path}: file does not contain a valid yaml hash
- Unable to parse %{message}
- Invalid data type in lookup_options for key '%{key}' could n
- The convert_to lookup_option for key '%{key}' raised error:
- all %{opts} patterns must match a key starting with module n
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/05ceb0600b8fddce.
Report an issue: GitHub.