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
:absentView on GitHub (pinned to e227c27540)
Solutions
- On hosts with old DNF, manage the packages with the yum/dnf providers instead: drop the module resources or pin `provider => 'yum'` for them.
- Gate the module resources by OS fact, e.g. only include them when $facts['os']['release']['major'] >= '8'.
- 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
- Select package providers from os.release.major facts in shared profiles instead of hardcoding dnfmodule.
- Keep module resources in EL8+ only code paths.
- Track dnf versions in your inventory so old-dnf hosts never receive module resources.
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
- Cannot have both `enable_only => true` and `flavor`
- Find %{uri} resulted in 404 with the message: %{body}
- Cannot have both `ensure => disabled` and `flavor`
- Cannot compare, as %{other} is not a Rpm Version
- PathPatterns cannot be created with a zero byte.
AI-assisted analysis of puppetlabs/puppet@e227c27540 (2026-08-21).
Data as JSON: /api/errors/7ec087d5ee7e7fa1.
Report an issue: GitHub.