puppetlabs/puppet · error · Puppet::ParseError

Cannot assign to a numeric match result variable '$%{name}'

Error message

Cannot assign to a numeric match result variable '$%{name}'

What it means

Raised by Scope#setvar when the variable name consists solely of digits (matches /^[0-9]+$/). Numeric variable names $0..$n are reserved for regex match captures set by MatchScope; they are populated automatically when a =~ or !~ expression with capture groups runs, and Puppet forbids explicit assignment to them. Note this guard lives in setvar, so it applies to DSL assignments and Ruby functions alike.

Source

Thrown at lib/puppet/parser/scope.rb:776

    end
  end
  private :transform_setting

  VARNAME_TRUSTED = 'trusted'
  VARNAME_FACTS = 'facts'
  VARNAME_SERVER_FACTS = 'server_facts'
  RESERVED_VARIABLE_NAMES = [VARNAME_TRUSTED, VARNAME_FACTS].freeze
  TYPENAME_CLASS = 'Class'
  TYPENAME_NODE = 'Node'

  # Set a variable in the current scope.  This will override settings
  # in scopes above, but will not allow variables in the current scope
  # to be reassigned.
  #   It's preferred that you use self[]= instead of this; only use this
  # when you need to set options.
  def setvar(name, value, options = EMPTY_HASH)
    if name =~ /^[0-9]+$/
      raise Puppet::ParseError, _("Cannot assign to a numeric match result variable '$%{name}'") % { name: name } # unless options[:ephemeral]
    end
    unless name.is_a? String
      raise Puppet::ParseError, _("Scope variable name %{name} is a %{class_type}, not a string") % { name: name.inspect, class_type: name.class }
    end

    # Check for reserved variable names
    if (name == VARNAME_TRUSTED || name == VARNAME_FACTS) && !options[:privileged]
      raise Puppet::ParseError, _("Attempt to assign to a reserved variable name: '%{name}'") % { name: name }
    end

    # Check for server_facts reserved variable name
    if name == VARNAME_SERVER_FACTS && !options[:privileged]
      raise Puppet::ParseError, _("Attempt to assign to a reserved variable name: '%{name}'") % { name: name }
    end

    table = effective_symtable(options[:ephemeral])
    if table.bound?(name)
      error = Puppet::ParseError.new(_("Cannot reassign variable '$%{name}'") % { name: name })

View on GitHub (pinned to e227c27540)

Solutions

  1. Rename the variable to a non-numeric name: `$match1` instead of `$1`.
  2. If you want regex captures, trigger them with a match expression instead of assigning: `$content =~ /(a+)/` then read $1.
  3. In Ruby functions, reject or prefix numeric names before setvar: `name = "v#{name}" if name =~ /^[0-9]+$/`.

Example fix

# before
$1 = 'primary'      # Cannot assign to a numeric match result variable '$1'

# after
$primary = 'primary'
# or set captures via an actual match:
'primary' =~ /^(.+)$/   # now $1 == 'primary'
Defensive patterns

Strategy: validation

Validate before calling

# Manifests: never use numeric names; if generating names programmatically:
#   name = "v#{raw}" if raw =~ /^[0-9]+$/
# Ruby guard before assignment:
raise ArgumentError, 'numeric var' if name =~ /^[0-9]+$/
scope.setvar(name, value) unless name =~ /^[0-9]+$/

Prevention

When it happens

Trigger: Manifest code `$1 = 'foo'` or `$0 = generate_title()`; Ruby code calling `scope.setvar('2', 'x')` or `scope['3'] = value` on a parser scope. Also a function looping over user-supplied names that happen to be numeric strings.

Common situations: Porting templates/scripts that used numbered variables as throwaway names; Ruby functions auto-creating variables from external data (ERB, YAML keys like '1') without sanitizing names; confusing numeric match variables with regular variables in examples copied from regex-heavy manifests.

Related errors


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