{"record":{"id":"ba429e90a97d3f0b","repo":"puppetlabs/puppet","slug":"failed-to-unload-user-profile-user","errorCode":null,"errorMessage":"Failed to unload user profile %{user}","messagePattern":"Failed to unload user profile %(.+?)","errorType":"exception","errorClass":"Puppet::Util::Windows::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/windows/user.rb","lineNumber":139,"sourceCode":"  private_class_method :logon_user_by_logon_type\n\n  def load_profile(user, password)\n    logon_user(user, password) do |token|\n      FFI::MemoryPointer.from_string_to_wide_string(user) do |lpUserName|\n        pi = PROFILEINFO.new\n        pi[:dwSize] = PROFILEINFO.size\n        pi[:dwFlags] = 1 # PI_NOUI - prevents display of profile error msgs\n        pi[:lpUserName] = lpUserName\n\n        # Load the profile. Since it doesn't exist, it will be created\n        if LoadUserProfileW(token, pi.pointer) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to load user profile %{user}\") % { user: user.inspect }\n        end\n\n        Puppet.debug(\"Loaded profile for #{user}\")\n\n        if UnloadUserProfile(token, pi[:hProfile]) == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, _(\"Failed to unload user profile %{user}\") % { user: user.inspect }\n        end\n      end\n    end\n  end\n  module_function :load_profile\n\n  def get_rights(name)\n    user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\\.\\\\/, \"#{Puppet::Util::Windows::ADSI.computer_name}\\\\\"))\n    return \"\" unless user_info\n\n    rights = []\n    rights_pointer = FFI::MemoryPointer.new(:pointer)\n    number_of_rights = FFI::MemoryPointer.new(:ulong)\n    sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes)\n\n    new_lsa_policy_handle do |policy_handle|\n      result = LsaEnumerateAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, number_of_rights)\n      check_lsa_nt_status_and_raise_failures(result, \"LsaEnumerateAccountRights\")","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/windows/user.rb#L121-L157","documentation":"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).","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nPuppet::Util::Windows::User.load_profile(user, password) do\n  run_some_code_that_opens_profile_resources # leaks handles -> unload fails\nend\n\n# after\nPuppet::Util::Windows::User.load_profile(user, password) do\n  run_some_code_that_opens_profile_resources\nensure\n  cleanup_opened_handles # close registry keys, files, COM objects\nend","handlingStrategy":"retry","validationCode":"# before unloading, confirm nothing in-process still holds profile resources\n# (close keys opened under HKU and files under the profile dir in the block)","typeGuard":null,"tryCatchPattern":"attempts = 0\nbegin\n  Puppet::Util::Windows::User.load_profile(user, password) { |token| do_work(token) }\nrescue Puppet::Util::Windows::Error => e\n  raise unless e.message.include?('Failed to unload user profile') && (attempts += 1) < 3\n  GC.start # prompt COM finalization of profile-backed objects\n  sleep 2 # let AV/indexer release transient handles\n  retry\nend","preventionTips":["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."],"tags":["windows","user-profile","registry-hive","handle-leak","puppet"],"backgroundTag":"user-profile-unload-failed","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}