puppetlabs/puppet · error · Puppet::Util::Windows::Error

Failed to remove environment variable: %{name}

Error message

Failed to remove environment variable: %{name}

What it means

Raised by set_environment_variable (lib/puppet/util/windows/process.rb:333) when the removal form of the API — SetEnvironmentVariableW(name, NULL) — returns FALSE. Windows answers with ERROR_ENVVAR_NOT_FOUND (203) when the variable is not present; other codes cover malformed names (embedded '=') or an environment the caller does not control. Removal is not idempotent at the Win32 level, so 'ensure absent' style workflows hit this naturally.

Source

Thrown at lib/puppet/util/windows/process.rb:333

                   end
                   .map { |env_pair| env_pair.split('=', 2) }
    pairs.to_h
  ensure
    if env_ptr && !env_ptr.null?
      if FreeEnvironmentStringsW(env_ptr) == FFI::WIN32_FALSE
        Puppet.debug "FreeEnvironmentStringsW memory leak"
      end
    end
  end
  module_function :get_environment_strings

  def set_environment_variable(name, val)
    raise Puppet::Util::Windows::Error(_('environment variable name must not be nil or empty')) if !name || name.empty?

    FFI::MemoryPointer.from_string_to_wide_string(name) do |name_ptr|
      if val.nil?
        if SetEnvironmentVariableW(name_ptr, FFI::MemoryPointer::NULL) == FFI::WIN32_FALSE
          raise Puppet::Util::Windows::Error, _("Failed to remove environment variable: %{name}") % { name: name }
        end
      else
        FFI::MemoryPointer.from_string_to_wide_string(val) do |val_ptr|
          if SetEnvironmentVariableW(name_ptr, val_ptr) == FFI::WIN32_FALSE
            raise Puppet::Util::Windows::Error, _("Failed to set environment variable: %{name}") % { name: name }
          end
        end
      end
    end
  end
  module_function :set_environment_variable

  def get_system_default_ui_language
    GetSystemDefaultUILanguage()
  end
  module_function :get_system_default_ui_language

  # Returns whether or not the OS has the ability to set elevated

View on GitHub (pinned to e227c27540)

Solutions

  1. Rescue Puppet::Util::Windows::Error and treat e.code == 203 (ERROR_ENVVAR_NOT_FOUND) as success for removal semantics.
  2. Check presence first via Process.get_environment_strings when you need explicit state transitions.
  3. Sanitize the name (no '=', sane length) before calling.
  4. For persistent machine/user variables, manage the registry environment keys and broadcast WM_SETTINGCHANGE instead of this per-process API.

Example fix

# before — raises when the variable is already absent
Process.set_environment_variable('MY_VAR', nil)

# after — idempotent removal
begin
  Process.set_environment_variable('MY_VAR', nil)
rescue Puppet::Util::Windows::Error => e
  raise unless e.code == 203 # ERROR_ENVVAR_NOT_FOUND — already gone
end
Defensive patterns

Strategy: try-catch

Validate before calling

# only attempt removal when present
present = Process.get_environment_strings.key?(name)
Process.set_environment_variable(name, nil) if present

Try / catch

begin
  Process.set_environment_variable(name, nil)
rescue Puppet::Util::Windows::Error => e
  raise unless e.code == 203 # ERROR_ENVVAR_NOT_FOUND
  # already absent — treat as success
end

Prevention

When it happens

Trigger: Removing a variable that was never set or was already removed earlier in the run (code 203); a name containing '='; unconditional cleanup calls in idempotent provisioning.

Common situations: Cleanup or provisioning scripts that remove variables unconditionally; a race with another writer deleting the variable first; hosts with differing environment baselines.

Related errors


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