{"record":{"id":"561fe7f2457c8e3e","repo":"puppetlabs/puppet","slug":"attempt-to-assign-to-a-reserved-variable-name","errorCode":null,"errorMessage":"Attempt to assign to a reserved variable name: '%{name}'","messagePattern":"Attempt to assign to a reserved variable name: '%(.+?)'","errorType":"exception","errorClass":"Puppet::ParseError","httpStatus":null,"severity":"error","filePath":"lib/puppet/parser/scope.rb","lineNumber":784,"sourceCode":"  TYPENAME_CLASS = 'Class'\n  TYPENAME_NODE = 'Node'\n\n  # Set a variable in the current scope.  This will override settings\n  # in scopes above, but will not allow variables in the current scope\n  # to be reassigned.\n  #   It's preferred that you use self[]= instead of this; only use this\n  # when you need to set options.\n  def setvar(name, value, options = EMPTY_HASH)\n    if name =~ /^[0-9]+$/\n      raise Puppet::ParseError, _(\"Cannot assign to a numeric match result variable '$%{name}'\") % { name: name } # unless options[:ephemeral]\n    end\n    unless name.is_a? String\n      raise Puppet::ParseError, _(\"Scope variable name %{name} is a %{class_type}, not a string\") % { name: name.inspect, class_type: name.class }\n    end\n\n    # Check for reserved variable names\n    if (name == VARNAME_TRUSTED || name == VARNAME_FACTS) && !options[:privileged]\n      raise Puppet::ParseError, _(\"Attempt to assign to a reserved variable name: '%{name}'\") % { name: name }\n    end\n\n    # Check for server_facts reserved variable name\n    if name == VARNAME_SERVER_FACTS && !options[:privileged]\n      raise Puppet::ParseError, _(\"Attempt to assign to a reserved variable name: '%{name}'\") % { name: name }\n    end\n\n    table = effective_symtable(options[:ephemeral])\n    if table.bound?(name)\n      error = Puppet::ParseError.new(_(\"Cannot reassign variable '$%{name}'\") % { name: name })\n      error.file = options[:file] if options[:file]\n      error.line = options[:line] if options[:line]\n      raise error\n    end\n\n    table[name] = value\n\n    # Assign the qualified name in the environment","sourceCodeStart":766,"sourceCodeEnd":802,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/parser/scope.rb#L766-L802","documentation":"Raised by Scope#setvar when assigning to the reserved names 'trusted' or 'facts' without options[:privileged]. These hashes are injected once by the compiler from node facts and certificate data; user code must not overwrite them because every consumer (manifests, functions, Hiera interpolation of $facts/$trusted) trusts their origin. Only internal calls marked :privileged may set them.","triggerScenarios":"Manifest assignments `$facts = {...}`, `$facts['kernel'] = 'Linux'`, or `$trusted = {...}`. Ruby functions calling `scope.setvar('facts', hash)` or `scope['trusted'] = data` without `privileged: true` in the options hash. Also templates or defined types trying to 'extend' the facts hash by reassigning the whole variable.","commonSituations":"Modules written for very old Puppet (pre-3.5) where $facts did not exist and users emulated it; attempts to fake facts in tests or local apply runs; copying ENC output into $trusted during provisioning scripts.","solutions":["Read $facts/$trusted instead of assigning; derive a local variable: `$myfacts = $facts + { 'extra' => 'v' }` ( Puppet 4 hash merge via +).","To add real facts, ship a custom fact (lib/facter/*.rb) so the value comes from the node itself.","For test harnesses, inject facts at the node level (e.g. puppet apply --loadclasses-style node definitions, rspec-puppet's let(:facts)) rather than assigning in the manifest.","If you truly are compiler-internal code, pass `privileged: true` to setvar."],"exampleFix":"# before\n$facts = { 'kernel' => 'Linux' }   # Attempt to assign to a reserved variable name: 'facts'\n\n# after\n$local_facts = $facts + { 'extra_info' => 'value' }\nnotify { $local_facts['kernel']: }","handlingStrategy":"validation","validationCode":"# Before writing code, check the reserved list:\nRESERVED = %w[trusted facts server_facts].freeze\nraise ArgumentError, \"reserved: #{n}\" if RESERVED.include?(n)\n# DSL: derive, don't assign:\n$local = $facts + { 'extra' => 'v' }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Treat $facts and $trusted as read-only node data; extend via custom facts (lib/facter) instead.","In Ruby functions, never call scope.setvar on reserved names; pass privileged: true only in compiler internals.","Lint for reserved-name assignments when upgrading old modules (rg '^\\$?(facts|trusted)\\s*=')."],"tags":["puppet","facts","trusted","reserved-names","scope"],"backgroundTag":"reserved-variable-name","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}