{"record":{"id":"153ce0bc8d720d65","repo":"puppetlabs/puppet","slug":"attempt-to-assign-variable-name-when-evaluating","errorCode":null,"errorMessage":"Attempt to assign variable %{name} when evaluating parameters","messagePattern":"Attempt to assign variable %(.+?) when evaluating parameters","errorType":"exception","errorClass":"Puppet::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/parser/scope.rb","lineNumber":234,"sourceCode":"    private :parameter_reference_failure\n\n    def initialize(parent, callee_name, param_names)\n      super(parent)\n      @callee_name = callee_name\n      @params = {}\n      param_names.each { |name| @params[name] = Access.new }\n    end\n\n    def [](name)\n      access = @params[name]\n      return super if access.nil?\n\n      throw(:unevaluated_parameter, name) unless access.assigned?\n      access.value\n    end\n\n    def []=(name, value)\n      raise Puppet::Error, _(\"Attempt to assign variable %{name} when evaluating parameters\") % { name: name } if @read_only\n\n      @params[name] ||= Access.new\n      @params[name].value = value\n    end\n\n    def bound?(name)\n      @params.include?(name)\n    end\n\n    def include?(name)\n      @params.include?(name) || super\n    end\n\n    def is_local_scope?\n      true\n    end\n\n    def as_read_only","sourceCodeStart":216,"sourceCodeEnd":252,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/parser/scope.rb#L216-L252","documentation":"Raised by ParameterScope#[]= while a parameter default expression is being evaluated. evaluate()/evaluate3x() wrap default evaluation in as_read_only, which sets @read_only = true; any write into a parameter during that window (variable assignment landing on the ParameterScope) is rejected. Its purpose is to keep default expressions side-effect free, since assigning to parameters mid-evaluation would corrupt the ordered evaluation contract.","triggerScenarios":"A parameter default whose evaluation assigns a scope variable that resolves into the ParameterScope — e.g. a legacy 3.x Ruby function called from a default expression that does scope.setvar / scope['x'] = ..., or an expression whose block/lambda shares the guarded scope and performs `$x = ...`. Example: `class c($a = set_scope_var('x'))` where the 3.x function writes scope['x']. API users hit it by calling ParameterScope#[]= directly while @read_only is true.","commonSituations":"Old 3.x-style modules whose Ruby functions mutate the scope being reused under Puppet 4+ parameter evaluation; wrapping default expressions with functions that cache results in scope variables; upgrading Puppet versions where evaluation order/read-only enforcement became stricter.","solutions":["Make the helper side-effect free: have the function RETURN a value used as the default instead of assigning a scope variable: `$a = my_func()`.","Move the assignment out of the default expression into the class body: `class c($a = undef) { $a = my_func() if $a == undef }`.","If you maintain the Ruby function, replace scope.setvar with a return value, or write to a named (non-parameter) scope explicitly."],"exampleFix":"# before (3.x function writes scope)\nmodule Puppet::Parser::Functions\n  newfunction(:set_port) do |args|\n    self.scope.setvar('port', args[0])   # raises when called from a default\n  end\nend\nclass app($port = set_port(8080)) { }\n\n# after\nmodule Puppet::Parser::Functions\n  newfunction(:get_port) do |args|\n    args[0]\n  end\nend\nclass app($port = get_port(8080)) { }","handlingStrategy":"validation","validationCode":"# Audit Ruby 3.x functions for scope mutation before reusing them in defaults:\n#   rg \"setvar|scope\\[.*\\]\\s*=\" lib/ \n# A default expression may only CALL functions that return values.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Keep parameter default expressions pure: no function that writes scope variables.","When writing 3.x functions, return values instead of scope.setvar; reserve setvar for legacy call sites.","Smoke-test classes with defaults in CI compilation so read-only violations surface before production."],"tags":["puppet","scope","parameters","side-effects","read-only"],"backgroundTag":"side-effects-in-default-expressions","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}