{"record":{"id":"9a3797e34a05c3ab","repo":"puppetlabs/puppet","slug":"ads-object-must-be-an-iadsuser-or-iadsgroup-instan","errorCode":null,"errorMessage":"ads_object must be an IAdsUser or IAdsGroup instance","messagePattern":"ads_object must be an IAdsUser or IAdsGroup instance","errorType":"exception","errorClass":"Puppet::Error","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/windows/sid.rb","lineNumber":119,"sourceCode":"      if !bytes || !bytes.respond_to?('pack') || bytes.empty?\n        raise Puppet::Util::Windows::Error, _(\"Octet string must be an array of bytes\")\n      end\n\n      Principal.lookup_account_sid(bytes)\n    end\n    module_function :octet_string_to_principal\n    class << self; alias octet_string_to_sid_object octet_string_to_principal; end\n\n    # Converts a COM instance of IAdsUser or IAdsGroup to a SID::Principal object,\n    # Raises an Error for nil or an object without an objectSID / Name property.\n    # This method returns a SID::Principal with the account, domain, SID, etc\n    # This method will return instances even when the SID is unresolvable, as\n    # may be the case when domain users have been added to local groups, but\n    # removed from the domain\n    def ads_to_principal(ads_object)\n      if !ads_object || !ads_object.respond_to?(:ole_respond_to?) ||\n         !ads_object.ole_respond_to?(:objectSID) || !ads_object.ole_respond_to?(:Name)\n        raise Puppet::Error, \"ads_object must be an IAdsUser or IAdsGroup instance\"\n      end\n\n      octet_string_to_principal(ads_object.objectSID)\n    rescue Puppet::Util::Windows::Error => e\n      # if the error is not a lookup / mapping problem, immediately re-raise\n      raise if e.code != ERROR_NONE_MAPPED\n\n      # if the Name property isn't formatted like a SID, OR\n      if !valid_sid?(ads_object.Name) ||\n         # if the objectSID doesn't match the Name property, also raise\n         ((converted = octet_string_to_sid_string(ads_object.objectSID)) != ads_object.Name)\n        raise Puppet::Error.new(\"ads_object Name: #{ads_object.Name} invalid or does not match objectSID: #{ads_object.objectSID} (#{converted})\", e)\n      end\n\n      unresolved_principal(ads_object.Name, ads_object.objectSID)\n    end\n    module_function :ads_to_principal\n","sourceCodeStart":101,"sourceCodeEnd":137,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/windows/sid.rb#L101-L137","documentation":"Raised by Puppet::Util::Windows::SID.ads_to_principal when the argument is nil, is not a WIN32OLE object (does not respond to ole_respond_to?), or lacks either the objectSID or Name property that IAdsUser/IAdsGroup expose. Unlike sibling errors this one is a non-translated plain Puppet::Error. The method exists to convert ADSI user/group COM objects into SID::Principal objects.","triggerScenarios":"Enumerating members of a WinNT:// group and passing each member to ads_to_principal: computer accounts (IAdsComputer) and schema objects do not have objectSID/Name in the required shape; passing the result of WIN32OLE.connect on a non-user/group path; passing a Ruby hash or nil instead of a COM object.","commonSituations":"Group members that are machines (NT4-style domains put computers in groups), one-off COM objects returned from ADSI queries (e.g. WinNT://WORKGROUP/LANMANSERVER), code refactors that swap in plain Ruby objects, nil returned by a failed ADSI bind.","solutions":["Filter before converting — only call ads_to_principal for members whose Class is 'User' or 'Group': `member.Class == 'User' || member.Class == 'Group'`.","Verify the ADSI path points at a user or group object (WinNT://DOMAIN/user,user).","Check for nil / failed WIN32OLE.connect results before use.","For non-user/group members, resolve them via name_to_principal instead."],"exampleFix":"# before\ngroup_members.each { |m| Puppet::Util::Windows::SID.ads_to_principal(m) }\n# raises for computer accounts in the group\n\n# after\ngroup_members.each do |m|\n  next unless %w[User Group].include?(m.Class)\n  Puppet::Util::Windows::SID.ads_to_principal(m)\nend","handlingStrategy":"type-guard","validationCode":"# only convert real user/group COM objects\nnext unless ads.respond_to?(:ole_respond_to?) &&\n             ads.ole_respond_to?(:objectSID) && ads.ole_respond_to?(:Name)","typeGuard":"def iads_user_or_group?(obj)\n  !obj.nil? &&\n    obj.respond_to?(:ole_respond_to?) &&\n    obj.ole_respond_to?(:objectSID) &&\n    obj.ole_respond_to?(:Name) &&\n    %w[User Group].include?(obj.Class rescue nil)\nend","tryCatchPattern":"begin\n  principal = Puppet::Util::Windows::SID.ads_to_principal(member)\nrescue Puppet::Error => e\n  raise unless e.message.include?('IAdsUser or IAdsGroup')\n  principal = Puppet::Util::Windows::SID.name_to_principal(member.Name) # resolve non-user/group members by name\nend","preventionTips":["Filter group enumerations by Class ('User'/'Group') before conversion — computer accounts and schema objects lack objectSID/Name.","Check WIN32OLE.connect results for nil before passing them on.","Keep a name-based fallback for foreign principals instead of assuming every member is user/group."],"tags":["windows","sid","adsi","win32ole","com","validation","puppet"],"backgroundTag":"win32ole-invalid-com-type","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}