ruby/ruby · error · Gem::DependencyError

Unresolved dependency found during sorting - #{dep} (request

Error message

Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})

What it means

During Gem::RequestSet's topological sort (tsort_each_child), every runtime dependency of each already-resolved spec is matched against the resolved requests via dep.match?. If no resolved spec satisfies a gem's dependency, Gem::DependencyError is raised - the resolved set is internally inconsistent. It is suppressed for shallow development dependencies and whenever soft_missing is enabled (e.g. `gem install --force`).

Source

Thrown at lib/rubygems/request_set.rb:513

    end
  end

  def tsort_each_node(&block) # :nodoc:
    @requests.each(&block)
  end

  def tsort_each_child(node) # :nodoc:
    node.spec.dependencies.each do |dep|
      next if dep.type == :development && !@development

      match = @requests.find do |r|
        dep.match?(r.spec.name, r.spec.version, r.spec.is_a?(Gem::Resolver::InstalledSpecification) || @prerelease)
      end

      unless match
        next if dep.type == :development && @development_shallow
        next if @soft_missing
        raise Gem::DependencyError,
              "Unresolved dependency found during sorting - #{dep} (requested by #{node.spec.full_name})"
      end

      yield match
    end
  end
end

require_relative "request_set/gem_dependency_api"
require_relative "request_set/lockfile"

View on GitHub (pinned to 0e5b888e1c)

Solutions

  1. Install the missing transitive gem first (its name/version appear in the message: 'Unresolved dependency found during sorting - <dep>') so the set can satisfy it
  2. Loosen version pins on the gem listed as 'requested by' so resolution can include the needed dependency version
  3. If missing deps are expected/acceptable, run with soft missing enabled: `gem install --force` (DependencyInstaller sets request_set.soft_missing = @force)
  4. For path:/git: gems, ensure their runtime dependencies are either committed to the tree or installable from the configured sources

Example fix

# before
$ gem install -g gems.rb   # => Unresolved dependency found during sorting - rake (>= 12) ...

# after
$ gem install 'rake:>= 12' && gem install -g gems.rb
# or, if missing deps are acceptable:
$ gem install -g gems.rb --force
Defensive patterns

Strategy: try-catch

Validate before calling

# pre-flight: ensure every transitive runtime dep of path/git gems resolves
deps = spec.runtime_dependencies.map { |d| "#{d.name} (#{d.requirement})" }
missing = deps.reject { |name, _| Gem::Specification.find_by_path(name) ||Gem::SpecFetcher.fetcher.spec_for_dependency(Gem::Dependency.new(name)).any? }

Try / catch

begin
  request_set.resolve
rescue Gem::DependencyError => e
  raise if !e.message.include?('Unresolved dependency')
  # extract dep from message and pre-install it, or accept partial install:
  # Gem::DependencyInstaller.new(force: true).install(dep_name)
end

Prevention

When it happens

Trigger: Resolving a set where a resolved gem's transitive dependency is absent from @requests: vendored/path/git gems whose dependencies are not available from any set in the composition; enabling development deps (`--development`) where a dev dependency is uninstallable; version constraints that excluded a needed transitive version from the requests.

Common situations: `gem install -g` with a gems.rb mixing path:/git: gems whose dependencies are not published; stale installed-spec sets (IndexSet of installed gems) missing one transitive gem; lockfile-style flows where one dependency was removed from the set manually; partial vendor trees.

Related errors


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