puppetlabs/puppet · error · Puppet::Util::Windows::Error
Failed to unload user profile %{user}
Error message
Failed to unload user profile %{user} What it means
Raised by Puppet::Util::Windows::User.load_profile when UnloadUserProfile fails after the profile was successfully loaded. Unloading decrements the hive's reference count; the call fails when other handles into the user's registry hive or profile directories are still open, so the hive stays loaded. Puppet::Util::Windows::Error attaches the Win32 reason (commonly ERROR_ACCESS_DENIED while the hive is in use).
Source
Thrown at lib/puppet/util/windows/user.rb:139
private_class_method :logon_user_by_logon_type
def load_profile(user, password)
logon_user(user, password) do |token|
FFI::MemoryPointer.from_string_to_wide_string(user) do |lpUserName|
pi = PROFILEINFO.new
pi[:dwSize] = PROFILEINFO.size
pi[:dwFlags] = 1 # PI_NOUI - prevents display of profile error msgs
pi[:lpUserName] = lpUserName
# Load the profile. Since it doesn't exist, it will be created
if LoadUserProfileW(token, pi.pointer) == FFI::WIN32_FALSE
raise Puppet::Util::Windows::Error, _("Failed to load user profile %{user}") % { user: user.inspect }
end
Puppet.debug("Loaded profile for #{user}")
if UnloadUserProfile(token, pi[:hProfile]) == FFI::WIN32_FALSE
raise Puppet::Util::Windows::Error, _("Failed to unload user profile %{user}") % { user: user.inspect }
end
end
end
end
module_function :load_profile
def get_rights(name)
user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\"))
return "" unless user_info
rights = []
rights_pointer = FFI::MemoryPointer.new(:pointer)
number_of_rights = FFI::MemoryPointer.new(:ulong)
sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes)
new_lsa_policy_handle do |policy_handle|
result = LsaEnumerateAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, number_of_rights)
check_lsa_nt_status_and_raise_failures(result, "LsaEnumerateAccountRights")View on GitHub (pinned to e227c27540)
Solutions
- Ensure every key/file/COM handle opened during the profile block is closed before the block returns.
- Close and retry once after a short delay — AV/indexer handles are often transient.
- If the hive remains loaded, unload it with `reg unload HKU\<SID>` from an elevated shell once nothing holds it.
- Identify the holder with Process Explorer / handle.exe on ntuser.dat or HKU subkeys.
- Reboot as a last resort for stubborn leaked handles.
Example fix
# before Puppet::Util::Windows::User.load_profile(user, password) do run_some_code_that_opens_profile_resources # leaks handles -> unload fails end # after Puppet::Util::Windows::User.load_profile(user, password) do run_some_code_that_opens_profile_resources ensure cleanup_opened_handles # close registry keys, files, COM objects end
Defensive patterns
Strategy: retry
Validate before calling
# before unloading, confirm nothing in-process still holds profile resources # (close keys opened under HKU and files under the profile dir in the block)
Try / catch
attempts = 0
begin
Puppet::Util::Windows::User.load_profile(user, password) { |token| do_work(token) }
rescue Puppet::Util::Windows::Error => e
raise unless e.message.include?('Failed to unload user profile') && (attempts += 1) < 3
GC.start # prompt COM finalization of profile-backed objects
sleep 2 # let AV/indexer release transient handles
retry
end Prevention
- Close every registry key, file handle, and COM object created under the profile inside the load_profile block (use ensure).
- Expect AV/indexing to hold profile files briefly; retry unload once or twice before failing.
- Monitor for leftover loaded hives (HKU\<SID> with no matching session) and clean them.
When it happens
Trigger: Something inside the yielded block (or elsewhere in the process) opened HKU\<SID> keys, files under the profile directory, or COM objects backed by the profile and did not close them before UnloadUserProfile ran; antivirus/indexing briefly holding profile files; the profile being in use by another logon session.
Common situations: Code calling load_profile and creating shell/COM objects without releasing them; previous failed runs leaving handles open; roaming profile contention between two machines/sessions; SCM-launched processes keeping the hive referenced.
Related errors
- Failed to load user profile %{user}
- RegisterEventSourceW failed to open Windows eventlog
- ReportEventW failed to report event to Windows eventlog
- Cannot determine basic system flavour
- Diff is not supported on this platform
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/ba429e90a97d3f0b.
Report an issue: GitHub.