ruby/ruby · error · ArgumentError

one set in the composition must be non-nil

Error message

one set in the composition must be non-nil

What it means

Gem::Resolver.compose_sets(*sets) compacts nil sets, flattens ComposedSets, and then requires at least one remaining resolver set; zero sets raises ArgumentError ('one set in the composition must be non-nil'). It is primarily an API-contract error for code driving the resolver directly - resolution needs at least one source of specifications.

Source

Thrown at lib/rubygems/resolver.rb:82

  # flattened into the result ComposedSet.

  def self.compose_sets(*sets)
    sets.compact!

    sets = sets.flat_map do |set|
      case set
      when Gem::Resolver::BestSet then
        set
      when Gem::Resolver::ComposedSet then
        set.sets
      else
        set
      end
    end

    case sets.length
    when 0 then
      raise ArgumentError, "one set in the composition must be non-nil"
    when 1 then
      sets.first
    else
      Gem::Resolver::ComposedSet.new(*sets)
    end
  end

  ##
  # Creates a Resolver that queries only against the already installed gems
  # for the +needed+ dependencies.

  def self.for_current_gems(needed)
    new needed, Gem::Resolver::CurrentSet.new
  end

  ##
  # Create Resolver object which will resolve the tree starting
  # with +needed+ Dependency objects.

View on GitHub (pinned to 0e5b888e1c)

Solutions

  1. Pass at least one concrete set, e.g. Gem::Resolver.compose_sets(Gem::Resolver::BestSet.new) or Gem::Resolver::IndexSet.new / InstalledSet.new / CurrentSet.new as appropriate
  2. Default nil sets to a sensible fallback before composing: sets << Gem::Resolver::BestSet.new if sets.compact.empty?
  3. If building from user config, validate that at least one source is enabled before constructing the resolver

Example fix

# before
set = Gem::Resolver.compose_sets(maybe_set)  # maybe_set is nil

# after
set = Gem::Resolver.compose_sets(maybe_set || Gem::Resolver::BestSet.new)
Defensive patterns

Strategy: type-guard

Validate before calling

sets = [git_set, vendor_set, index_set].compact
sets = [Gem::Resolver::BestSet.new] if sets.empty?
resolver_set = Gem::Resolver.compose_sets(*sets)

Type guard

def resolvable_set?(set)
  set.is_a?(Gem::Resolver::IndexSet) || set.is_a?(Gem::Resolver::BestSet) || set.is_a?(Gem::Resolver::ComposedSet) || set.is_a?(Gem::Resolver::InstalledSet) || set.is_a?(Gem::Resolver::CurrentSet) || set.is_a?(Gem::Resolver::VendorSet) || set.is_a?(Gem::Resolver::GitSet) || set.is_a?(Gem::Resolver::LockfileSet) || set.is_a?(Gem::Resolver::SourceSet)
end

Try / catch

begin
  set = Gem::Resolver.compose_sets(*sets)
rescue ArgumentError => e
  raise if !e.message.include?('non-nil')
  set = Gem::Resolver.compose_sets(Gem::Resolver::BestSet.new)
end

Prevention

When it happens

Trigger: Gem::Resolver.compose_sets(nil), compose_sets() with no args, or compose_sets(*array) where array contains only nils; also higher-level tooling that builds sets from optional inputs (e.g. all sources disabled) and forwards an empty list to the Resolver.

Common situations: Custom installers/dependency tooling over RubyGems internals; configurations where every source is disabled (all `gem sources` removed and no local sets supplied); guard-of-absent-arguments refactors that pass nil instead of a default set.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of ruby/ruby@0e5b888e1c (2026-08-21). Data as JSON: /api/errors/18d19c66841e59da. Report an issue: GitHub.