puppetlabs/puppet · error · Puppet::Error

Modules are not supported on DNF versions lower than 3.0.1

Error message

Modules are not supported on DNF versions lower than 3.0.1

What it means

The dnfmodule provider (dnfmodule.rb:30) compares the first token of `dnf --version` against '3.0.1' during prefetch and raises Puppet::Error when versioncmp says the installed DNF is older. DNF module commands (dnf module list/install) are only reliable from 3.0.1 onwards, so on older DNF (RHEL 7 era dnf, Fedora < 27) the provider refuses to manage module packages.

Source

Thrown at lib/puppet/provider/package/dnfmodule.rb:30

require_relative '../../../puppet/provider/package'

Puppet::Type.type(:package).provide :dnfmodule, :parent => :dnf do
  has_feature :installable, :uninstallable, :versionable, :supports_flavors, :disableable
  # has_feature :upgradeable
  # it's not (yet) feasible to make this upgradeable since module streams don't
  # always have matching version types (i.e. idm has streams DL1 and client,
  # other modules have semver streams, others have string streams... we cannot
  # programatically determine a latest version for ensure => 'latest'

  commands :dnf => '/usr/bin/dnf'

  def self.current_version
    @current_version ||= dnf('--version').split.first
  end

  def self.prefetch(packages)
    if Puppet::Util::Package.versioncmp(current_version, '3.0.1') < 0
      raise Puppet::Error, _("Modules are not supported on DNF versions lower than 3.0.1")
    end

    super
  end

  def self.instances
    packages = []
    cmd = "#{command(:dnf)} module list -y -d 0 -e #{error_level}"
    execute(cmd).each_line do |line|
      # select only lines with actual packages since DNF clutters the output
      next unless line =~ /\[[eix]\][, ]/

      line.gsub!(/\[d\]/, '') # we don't care about the default flag

      flavor = if line.include?('[i]')
                 line.split('[i]').first.split.last
               else
                 :absent

View on GitHub (pinned to e227c27540)

Solutions

  1. On hosts with old DNF, manage the packages with the yum/dnf providers instead: drop the module resources or pin `provider => 'yum'` for them.
  2. Gate the module resources by OS fact, e.g. only include them when $facts['os']['release']['major'] >= '8'.
  3. Or upgrade the platform to one shipping DNF >= 3.0.1 (EL8+) where dnfmodule is supported.

Example fix

# before
package { 'nodejs:14': ensure => 'enabled', provider => 'dnfmodule' }

# after
if $facts['os']['release']['major'] >= '8' {
  package { 'nodejs:14': ensure => 'enabled', provider => 'dnfmodule' }
} else {
  package { 'nodejs': ensure => installed, provider => 'yum' }
}
Defensive patterns

Strategy: validation

Validate before calling

# check dnf version before selecting dnfmodule
v = `dnf --version 2>/dev/null`.split.first
use_module = v && Puppet::Util::Package.versioncmp(v, '3.0.1') >= 0
fail 'dnfmodule needs dnf >= 3.0.1' unless use_module

Type guard

def supports_dnf_modules?(facts)
  facts.dig('os', 'family') == 'RedHat' && facts.dig('os', 'release', 'major').to_i >= 8
end

Try / catch

begin
  provider.prefetch(packages)
rescue Puppet::Error => e
  raise unless e.message.include?('lower than 3.0.1')
  packages.each { |p| p[:provider] = :yum if p[:provider] == :dnfmodule }
end

Prevention

When it happens

Trigger: Any catalog containing a package resource handled by dnfmodule (usually ensure => installed with a module-flavored name or an explicitly selected provider => dnfmodule) on a host whose `dnf --version` reports < 3.0.1. The check runs in prefetch, so it fires even when the module resource is already in the desired state.

Common situations: RHEL/CentOS 7 systems with the dnf Copr installed; EL7 modules accidentally included in a shared profile that also targets EL8; container base images with an old dnf; someone setting provider => dnfmodule on an unsupported box.

Related errors


AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21). Data as JSON: /api/errors/7ec087d5ee7e7fa1. Report an issue: GitHub.