{"record":{"id":"60117a318801f45c","repo":"puppetlabs/puppet","slug":"undefined-variable-name-reason","errorCode":null,"errorMessage":"Undefined variable '%{name}'; %{reason}","messagePattern":"Undefined variable '%(.+?)'; %(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/hiera/scope.rb","lineNumber":52,"sourceCode":"    end\n\n    # This method is used to handle the throw of :undefined_variable since when\n    # strict variables is not in effect, missing handling of the throw leads to\n    # a more expensive code path.\n    #\n    def safe_lookupvar(key)\n      reason = catch :undefined_variable do\n        return @real.lookupvar(key)\n      end\n\n      case Puppet[:strict]\n      when :off\n        # do nothing\n      when :warning\n        Puppet.warn_once(Puppet::Parser::Scope::UNDEFINED_VARIABLES_KIND, _(\"Variable: %{name}\") % { name: key },\n                         _(\"Undefined variable '%{name}'; %{reason}\") % { name: key, reason: reason })\n      when :error\n        raise ArgumentError, _(\"Undefined variable '%{name}'; %{reason}\") % { name: key, reason: reason }\n      end\n      nil\n    end\n    private :safe_lookupvar\n\n    def exist?(key)\n      CALLING_KEYS.include?(key) || @real.exist?(key)\n    end\n\n    def include?(key)\n      CALLING_KEYS.include?(key) || @real.include?(key)\n    end\n\n    def catalog\n      @real.catalog\n    end\n\n    def resource","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/hiera/scope.rb#L34-L70","documentation":"Hiera::Scope#safe_lookupvar wraps the real puppet scope's lookupvar; when the scope rejects the lookup it throws :undefined_variable with a reason, and with Puppet[:strict] = :error the catch block converts that into an ArgumentError naming the variable and reason. At :warning it emits a warn_once notice instead; at :off it silently returns nil.","triggerScenarios":"Interpolating a never-assigned variable in hiera data (%{undefined_var}) or calling lookupvar for a missing name while running with strict_variables = true and strict = error (the Puppet 8 defaults).","commonSituations":"Upgrading manifests from older Puppet's loose variable behavior to strict_variables; typos in variable names inside hiera YAML interpolation; relying on facts or class variables that are only set on some node roles.","solutions":["Set the variable (or fix its spelling) before the lookup — assign it in the manifest/profile","Make absence legal by giving the lookup a fallback: lookup('my::key', 'default') or hiera('key', 'default')","Temporarily relax strictness in puppet.conf: strict = warning (and/or strict_variables = false) while migrating","Run puppet lookup --explain to see exactly which variable lookup fails and why"],"exampleFix":"# data/myapp.yaml\n# before\nserver: \"%{undefined_hostname}\"   # strict=error -> ArgumentError\n\n# after\nserver: \"%{lookup('facts.networking.hostname', 'unknown')}\"","handlingStrategy":"validation","validationCode":"key = 'my_var'\nunless scope.exist?(key)  # Hiera::Scope#exist? / include? are public\n  Puppet.warning(\"skipping lookup of undefined #{key}\")\n  return nil\nend\nscope.lookupvar(key)","typeGuard":null,"tryCatchPattern":"begin\n  value = scope.lookupvar(key)\nrescue ArgumentError => e\n  raise unless e.message =~ /Undefined variable/\n  default_value # strict=error path: degrade to a default\nend","preventionTips":["Run with strict = warning in CI first to surface all undefined variables before enabling error","Use scope.exist?(key)/include?(key) before lookups on optional data","Always give lookup()/hiera() calls a default when absence is legitimate"],"tags":["hiera","variables","scope","puppet"],"backgroundTag":"undefined-variable","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}