{"record":{"id":"2e471a9085136166","repo":"puppetlabs/puppet","slug":"the-getgrouplist-method-is-not-available","errorCode":null,"errorMessage":"The 'getgrouplist' method is not available","messagePattern":"The 'getgrouplist' method is not available","errorType":"exception","errorClass":"LoadError","httpStatus":null,"severity":"error","filePath":"lib/puppet/util/posix.rb","lineNumber":39,"sourceCode":"\n        groups = []\n        Puppet::Etc.group do |group|\n          groups << group.name if group.mem.include?(user)\n        end\n      end\n\n      uniq_groups = groups.uniq\n      if uniq_groups != groups\n        Puppet.debug(_('Removing any duplicate group entries'))\n      end\n\n      uniq_groups\n    end\n\n    private\n\n    def get_groups_list(user)\n      raise LoadError, \"The 'getgrouplist' method is not available\" unless Puppet::FFI::POSIX::Functions.respond_to?(:getgrouplist)\n\n      user_gid = Puppet::Etc.getpwnam(user).gid\n      ngroups = Puppet::FFI::POSIX::Constants::MAXIMUM_NUMBER_OF_GROUPS\n\n      loop do\n        FFI::MemoryPointer.new(:int) do |ngroups_ptr|\n          FFI::MemoryPointer.new(:uint, ngroups) do |groups_ptr|\n            old_ngroups = ngroups\n            ngroups_ptr.write_int(ngroups)\n\n            if Puppet::FFI::POSIX::Functions.getgrouplist(user, user_gid, groups_ptr, ngroups_ptr) != -1\n              groups_gids = groups_ptr.get_array_of_uint(0, ngroups_ptr.read_int)\n\n              result = []\n              groups_gids.each do |group_gid|\n                group_info = Puppet::Etc.getgrgid(group_gid)\n                result |= [group_info.name] if group_info.mem.include?(user)\n              end","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/puppetlabs/puppet/blob/e227c27540975c25aa22d533a52424a9d2fc886a/lib/puppet/util/posix.rb#L21-L57","documentation":"Puppet::Util::POSIX#get_groups_list (posix.rb:39) raises LoadError when Puppet::FFI::POSIX::Functions does not respond to getgrouplist, i.e. the FFI binding for getgrouplist(3) is absent (platform without the libc call, ffi gem missing/broken, or restricted load). Important context: the public caller groups_of (posix.rb:15-26) rescues StandardError and LoadError, logs 'Falling back to Puppet::Etc.group', and iterates the group database instead, so seeing this error escape means someone called the private helper directly or the fallback path itself is broken.","triggerScenarios":"Calling get_groups_list/send(:get_groups_list, user) directly on Windows (no getgrouplist), on a platform where the ffi gem failed to install, or when libffi cannot bind the symbol (hardened libc, missing headers at gem build time). Calling groups_of on such a platform only logs a debug fallback, it does not raise.","commonSituations":"Running Puppet code on unsupported/minimal containers (alpine without libffi), FFI gem compiled against an incompatible libc after a system upgrade, custom user/group providers invoking POSIX groups handling outside Puppet's own guarded path.","solutions":["Use the public Puppet::Util::POSIX.groups_of(user), which already falls back to Puppet::Etc.group iteration.","Ensure the ffi gem is installed and loadable: gem install ffi; ruby -e \"require 'ffi'\".","On the problem host, verify the symbol exists: ruby -e \"require 'puppet/ffi/posix'; p Puppet::FFI::POSIX::Functions.respond_to?(:getgrouplist)\".","If the platform genuinely lacks getgrouplist (e.g., Windows), branch to Puppet::Util::Windows or Etc-based enumeration instead of the FFI path."],"exampleFix":"// before\ngroups = obj.send(:get_groups_list, user) # bypasses the guarded path, raises LoadError\n\n// after\ngroups = Puppet::Util::POSIX.groups_of(user) # FFI path with automatic Etc.group fallback","handlingStrategy":"validation","validationCode":"require 'puppet/ffi/posix' rescue nil\nffi_ok = defined?(Puppet::FFI::POSIX::Functions) && Puppet::FFI::POSIX::Functions.respond_to?(:getgrouplist)","typeGuard":null,"tryCatchPattern":"begin\n  groups = Puppet::Util::POSIX.groups_of(user)\nrescue LoadError => e\n  Puppet.warning(\"FFI group lookup unavailable (#{e.message}); using Etc fallback\")\n  groups = Puppet::Etc.group.select { |g| g.mem.include?(user) }.map(&:name)\nend","preventionTips":["Always call the public groups_of; it has the built-in Etc fallback.","Smoke-test ffi on new images: ruby -e \"require 'ffi'\".","Branch per-platform before touching POSIX FFI helpers."],"tags":["posix","ffi","groups","user-lookup","platform-support","loaderror"],"backgroundTag":"native-function-unavailable","analyzedSha":"e227c27540975c25aa22d533a52424a9d2fc886a","analyzedAt":"2026-08-21T20:49:46.650Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}