puppetlabs/puppet · error · ArgumentError

Undefined variable '%{name}'; %{reason}

Error message

Undefined variable '%{name}'; %{reason}

What it means

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.

Source

Thrown at lib/hiera/scope.rb:52

    end

    # This method is used to handle the throw of :undefined_variable since when
    # strict variables is not in effect, missing handling of the throw leads to
    # a more expensive code path.
    #
    def safe_lookupvar(key)
      reason = catch :undefined_variable do
        return @real.lookupvar(key)
      end

      case Puppet[:strict]
      when :off
        # do nothing
      when :warning
        Puppet.warn_once(Puppet::Parser::Scope::UNDEFINED_VARIABLES_KIND, _("Variable: %{name}") % { name: key },
                         _("Undefined variable '%{name}'; %{reason}") % { name: key, reason: reason })
      when :error
        raise ArgumentError, _("Undefined variable '%{name}'; %{reason}") % { name: key, reason: reason }
      end
      nil
    end
    private :safe_lookupvar

    def exist?(key)
      CALLING_KEYS.include?(key) || @real.exist?(key)
    end

    def include?(key)
      CALLING_KEYS.include?(key) || @real.include?(key)
    end

    def catalog
      @real.catalog
    end

    def resource

View on GitHub (pinned to e227c27540)

Solutions

  1. Set the variable (or fix its spelling) before the lookup — assign it in the manifest/profile
  2. Make absence legal by giving the lookup a fallback: lookup('my::key', 'default') or hiera('key', 'default')
  3. Temporarily relax strictness in puppet.conf: strict = warning (and/or strict_variables = false) while migrating
  4. Run puppet lookup --explain to see exactly which variable lookup fails and why

Example fix

# data/myapp.yaml
# before
server: "%{undefined_hostname}"   # strict=error -> ArgumentError

# after
server: "%{lookup('facts.networking.hostname', 'unknown')}"
Defensive patterns

Strategy: validation

Validate before calling

key = 'my_var'
unless scope.exist?(key)  # Hiera::Scope#exist? / include? are public
  Puppet.warning("skipping lookup of undefined #{key}")
  return nil
end
scope.lookupvar(key)

Try / catch

begin
  value = scope.lookupvar(key)
rescue ArgumentError => e
  raise unless e.message =~ /Undefined variable/
  default_value # strict=error path: degrade to a default
end

Prevention

When it happens

Trigger: 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).

Common situations: 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.

Related errors


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