puppetlabs/puppet · error · Puppet::Error
ads_object must be an IAdsUser or IAdsGroup instance
Error message
ads_object must be an IAdsUser or IAdsGroup instance
What it means
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.
Source
Thrown at lib/puppet/util/windows/sid.rb:119
if !bytes || !bytes.respond_to?('pack') || bytes.empty?
raise Puppet::Util::Windows::Error, _("Octet string must be an array of bytes")
end
Principal.lookup_account_sid(bytes)
end
module_function :octet_string_to_principal
class << self; alias octet_string_to_sid_object octet_string_to_principal; end
# Converts a COM instance of IAdsUser or IAdsGroup to a SID::Principal object,
# Raises an Error for nil or an object without an objectSID / Name property.
# This method returns a SID::Principal with the account, domain, SID, etc
# This method will return instances even when the SID is unresolvable, as
# may be the case when domain users have been added to local groups, but
# removed from the domain
def ads_to_principal(ads_object)
if !ads_object || !ads_object.respond_to?(:ole_respond_to?) ||
!ads_object.ole_respond_to?(:objectSID) || !ads_object.ole_respond_to?(:Name)
raise Puppet::Error, "ads_object must be an IAdsUser or IAdsGroup instance"
end
octet_string_to_principal(ads_object.objectSID)
rescue Puppet::Util::Windows::Error => e
# if the error is not a lookup / mapping problem, immediately re-raise
raise if e.code != ERROR_NONE_MAPPED
# if the Name property isn't formatted like a SID, OR
if !valid_sid?(ads_object.Name) ||
# if the objectSID doesn't match the Name property, also raise
((converted = octet_string_to_sid_string(ads_object.objectSID)) != ads_object.Name)
raise Puppet::Error.new("ads_object Name: #{ads_object.Name} invalid or does not match objectSID: #{ads_object.objectSID} (#{converted})", e)
end
unresolved_principal(ads_object.Name, ads_object.objectSID)
end
module_function :ads_to_principal
View on GitHub (pinned to e227c27540)
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.
Example fix
# before
group_members.each { |m| Puppet::Util::Windows::SID.ads_to_principal(m) }
# raises for computer accounts in the group
# after
group_members.each do |m|
next unless %w[User Group].include?(m.Class)
Puppet::Util::Windows::SID.ads_to_principal(m)
end Defensive patterns
Strategy: type-guard
Validate before calling
# only convert real user/group COM objects
next unless ads.respond_to?(:ole_respond_to?) &&
ads.ole_respond_to?(:objectSID) && ads.ole_respond_to?(:Name) Type guard
def iads_user_or_group?(obj)
!obj.nil? &&
obj.respond_to?(:ole_respond_to?) &&
obj.ole_respond_to?(:objectSID) &&
obj.ole_respond_to?(:Name) &&
%w[User Group].include?(obj.Class rescue nil)
end Try / catch
begin
principal = Puppet::Util::Windows::SID.ads_to_principal(member)
rescue Puppet::Error => e
raise unless e.message.include?('IAdsUser or IAdsGroup')
principal = Puppet::Util::Windows::SID.name_to_principal(member.Name) # resolve non-user/group members by name
end Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Octet string must be an array of bytes
- Value must be in DOMAIN\\%{object_class} style syntax
- Could not resolve name: %{name}
- Unrecognized ADS UserFlags: %{unrecognized_flags}
- Byte array for lookup_account_sid must not be nil and must b
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/9a3797e34a05c3ab.
Report an issue: GitHub.