{"record":{"id":"3c520fc75a806a69","repo":"puppetlabs/puppet","slug":"gettokeninformation-token-handle-token-infor-3c520f","errorCode":null,"errorMessage":"GetTokenInformation(#{token_handle}, #{token_information}, #{token_information_buf}, #{return_length}, #{return_length_ptr})","messagePattern":"GetTokenInformation\\(#(.+?), #(.+?), #(.+?), #(.+?), #(.+?)\\)","errorType":"exception","errorClass":"Puppet::Util::Windows::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/windows/process.rb","lineNumber":185,"sourceCode":"  module_function :lookup_privilege_value\n\n  def get_token_information(token_handle, token_information, &block)\n    # to determine buffer size\n    FFI::MemoryPointer.new(:dword, 1) do |return_length_ptr|\n      result = GetTokenInformation(token_handle, token_information, nil, 0, return_length_ptr)\n      return_length = return_length_ptr.read_dword\n\n      if return_length <= 0\n        raise Puppet::Util::Windows::Error, \"GetTokenInformation(#{token_handle}, #{token_information}, nil, 0, #{return_length_ptr})\"\n      end\n\n      # re-call API with properly sized buffer for all results\n      FFI::MemoryPointer.new(return_length) do |token_information_buf|\n        result = GetTokenInformation(token_handle, token_information,\n                                     token_information_buf, return_length, return_length_ptr)\n\n        if result == FFI::WIN32_FALSE\n          raise Puppet::Util::Windows::Error, \"GetTokenInformation(#{token_handle}, #{token_information}, #{token_information_buf}, \" \\\n                                              \"#{return_length}, #{return_length_ptr})\"\n        end\n\n        yield token_information_buf\n      end\n    end\n\n    # GetTokenInformation buffer has been cleaned up by this point, nothing to return\n    nil\n  end\n  module_function :get_token_information\n\n  def parse_token_information_as_token_privileges(token_information_buf)\n    raw_privileges = TOKEN_PRIVILEGES.new(token_information_buf)\n    privileges = { :count => raw_privileges[:PrivilegeCount], :privileges => [] }\n\n    offset = token_information_buf + TOKEN_PRIVILEGES.offset_of(:Privileges)\n    privilege_ptr = FFI::Pointer.new(LUID_AND_ATTRIBUTES, offset)","sourceCodeStart":167,"sourceCodeEnd":203,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/windows/process.rb#L167-L203","documentation":"The second failure mode of Process.get_token_information (lib/puppet/util/windows/process.rb:185): the sizing call succeeded (return_length > 0) but the follow-up GetTokenInformation with the allocated buffer returned FALSE. Something changed between the two back-to-back calls — the handle was closed concurrently, or the token was invalidated as its owning process exited. Usually a transient race rather than a permanent condition.","triggerScenarios":"The token handle being closed concurrently while get_token_information runs (threads sharing handles); the owning process exiting mid-query; the buffer sized from a return_length that went stale between calls.","commonSituations":"Multithreaded code sharing one token handle across workers; intermittent failures under load that a retry fixes; long-lived handles stashed from an earlier open instead of being re-opened.","solutions":["Retry the whole query once — this failure is usually a transient race.","Eliminate sharing: open a fresh token per thread or consumer instead of passing handles around.","Keep the query inside the open_process_token block so the handle cannot be closed mid-flight.","Read e.code to distinguish handle races (6) from access problems (5)."],"exampleFix":"# before — one shot, fails intermittently under threads\nProcess.get_token_information(token, :TokenPrivileges) { |buf| use(buf) }\n\n# after — one bounded retry\nbegin\n  Process.get_token_information(token, :TokenPrivileges) { |buf| use(buf) }\nrescue Puppet::Util::Windows::Error\n  sleep 0.01\n  Process.get_token_information(token, :TokenPrivileges) { |buf| use(buf) }\nend","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"attempts = 0\nbegin\n  attempts += 1\n  Process.get_token_information(token, info_class) { |buf| yield buf }\nrescue Puppet::Util::Windows::Error => e\n  retry if attempts < 2 && e.code == 6 # transient handle race\n  raise\nend","preventionTips":["Do not share token handles across threads — open one per consumer","Retry once on ERROR_INVALID_HANDLE races before surfacing the error","Keep queries within the open_process_token block"],"tags":["windows","win32-api","token","race-condition","ffi","puppet"],"backgroundTag":"win32-gettokeninformation-failed","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}